summaryrefslogtreecommitdiff
path: root/pexpect/ansi.py
diff options
context:
space:
mode:
Diffstat (limited to 'pexpect/ansi.py')
-rwxr-xr-xpexpect/ansi.py347
1 files changed, 347 insertions, 0 deletions
diff --git a/pexpect/ansi.py b/pexpect/ansi.py
new file mode 100755
index 0000000..ed0d94e
--- /dev/null
+++ b/pexpect/ansi.py
@@ -0,0 +1,347 @@
+#!/usr/bin/env python
+
+# references:
+# http://www.retards.org/terminals/vt102.html
+# http://vt100.net/docs/vt102-ug/contents.html
+
+import copy
+
+NUL = 0 # Fill character; ignored on input.
+ENQ = 5 # Transmit answerback message.
+BEL = 7 # Ring the bell.
+BS = 8 # Move cursor left.
+HT = 9 # Move cursor to next tab stop.
+LF = 10 # Line feed.
+VT = 11 # Same as LF.
+FF = 12 # Same as LF.
+CR = 13 # Move cursor to left margin or newline.
+SO = 14 # Invoke G1 character set.
+SI = 15 # Invoke G0 character set.
+XON = 17 # Resume transmission.
+XOFF = 19 # Halt transmission.
+CAN = 24 # Cancel escape sequence.
+SUB = 26 # Same as CAN.
+ESC = 27 # Introduce a control sequence.
+DEL = 127 # Fill character; ignored on input.
+SPACE = chr(32) # Space or blank character.
+
+import fsm
+
+def constrain (n, min, max):
+ if n < min:
+ return min
+ if n > max:
+ return max
+ return n
+
+def push_digit (input_symbol, state, stack):
+ stack.append(input_symbol)
+def build_digit (input_symbol, state, stack):
+ s = stack.pop()
+ s = s + input_symbol
+ stack.append(s)
+def accept (input_symbol, state, stack):
+ if input_symbol=='H':
+ c = stack.pop()
+ r = stack.pop()
+ print 'HOME (r,c) -> (%s, %s)' % (r,c)
+ if input_symbol == 'D':
+ n = stack.pop()
+ print 'BACK (n) -> %s' % n
+ if input_symbol == 'B':
+ n = stack.pop()
+ print 'DOWN (n) -> %s' % n
+def default (input_symbol, state, stack):
+ print 'UNDEFINED: %s, %s -- RESETTING' % (input_symbol, state)
+ stack=[]
+
+
+class term:
+ '''This class encapsulates a generic terminal.
+ It filters a stream and maintains the state of
+ a screen object.
+ '''
+ def __init__ (self):
+ s = fsm ('INIT')
+ f.add_default_transition ('INIT', default)
+ f.add_transition('INIT','\x1b', 'ESC', None)
+ f.add_transition('ESC','[', 'ELB', None)
+ f.add_transition_list('ELB',['0','1','2','3','4','5','6','7','8','9'], 'ELB_DIGIT', push_digit)
+ f.add_transition_list('ELB_DIGIT',['0','1','2','3','4','5','6','7','8','9'], 'ELB_DIGIT', build_digit)
+ f.add_transition('ELB_DIGIT',';', 'SEMICOLON', None)
+ f.add_transition_list('SEMICOLON',['0','1','2','3','4','5','6','7','8','9'], 'ELB_DIGIT2', push_digit)
+ f.add_transition_list('ELB_DIGIT2',['0','1','2','3','4','5','6','7','8','9'], 'ELB_DIGIT2', build_digit)
+ f.add_transition_list('ELB_DIGIT2',['H','f'], 'INIT', accept)
+ f.add_transition_list('ELB_DIGIT',['D','B','C','A'], 'INIT', accept)
+
+
+
+class screen:
+ def __init__ (self, r=24,c=80):
+ self.rows = r
+ self.cols = c
+ self.cur_r = 1
+ self.cur_c = 1
+ self.scroll_row_start = 1
+ self.scroll_row_end = self.rows
+ self.mode_scape = 0
+ self.w = [ [SPACE] * self.cols for c in range(self.rows)]
+
+ def __str__ (self):
+ s = ''
+ for r in range (1, self.rows + 1):
+ for c in range (1, self.cols + 1):
+ s = s + self.get(r,c)
+ s = s + '\n'
+ return s
+
+ def fill (self, ch=SPACE):
+ self.fill_region (1,1,self.rows,self.cols, ch)
+
+ def fill_region (self, rs,cs, re,ce, ch=SPACE):
+ rs = constrain (rs, 1, self.rows)
+ re = constrain (re, 1, self.rows)
+ cs = constrain (cs, 1, self.cols)
+ ce = constrain (ce, 1, self.cols)
+ temp=0
+ if rs > re:
+ temp = re
+ re = rs
+ rs = temp
+ if cs > ce:
+ temp = ce
+ ce = cs
+ cs = temp
+ for r in range (rs, re+1):
+ for c in range (cs, ce + 1):
+ self.put (r,c,ch)
+
+ def type (self, ch):
+ '''Puts a character at the current cursor position.
+ cursor position if moved forward with wrap-around, but
+ no scrolling is done if the cursor hits the lower-right corner
+ of the screen.
+ '''
+ if ch == '\r':
+ self.crlf()
+ return
+
+ self.put(self.cur_r, self.cur_c, ch)
+ old_r = self.cur_r
+ old_c = self.cur_c
+ self.cursor_forward()
+ if old_c == self.cur_c:
+ self.cursor_down()
+ if old_r != self.cur_r:
+ self.cursor_home (self.cur_r, 1)
+ else:
+ self.scroll_up ()
+ self.cursor_home (self.cur_r, 1)
+ self.erase_line()
+
+ def crlf (self):
+ '''This advances the cursor with CRLF properties.
+ The cursor will line wrap and the screen may scroll.
+ Under UNIX this is what happens when a chr(13) '\r' is read.
+ '''
+ self.cursor_home (self.cur_r, 1)
+ old_r = self.cur_r
+ self.cursor_down()
+ if old_r == self.cur_r:
+ self.scroll_up ()
+ self.erase_line()
+
+ def put (self, r, c, ch):
+ '''Screen array starts at 1 index.'''
+# if r < 1 or r > self.rows or c < 1 or c > self.cols:
+# raise IndexError ('Screen array index out of range')
+ ch = str(ch)[0]
+ self.w[r-1][c-1] = ch
+
+ def get (self, r, c):
+ '''Screen array starts at 1 index.'''
+# if r < 1 or r > self.rows or c < 1 or c > self.cols:
+# raise IndexError ('Screen array index out of range')
+ return self.w[r-1][c-1]
+
+ def cursor_constrain (self):
+ self.cur_r = constrain (self.cur_r, 1, self.rows)
+ self.cur_c = constrain (self.cur_c, 1, self.cols)
+
+ def cursor_home (self, r=1, c=1): # <ESC>[{ROW};{COLUMN}H
+ self.cur_r = r
+ self.cur_c = c
+ self.cursor_constrain ()
+ def cursor_back (self,count=1): # <ESC>[{COUNT}D (not confused with down)
+ self.cur_r = self.cur_r - count
+ self.cursor_constrain ()
+ def cursor_down (self,count=1): # <ESC>[{COUNT}B (not confused with back)
+ self.cur_r = self.cur_r + count
+ self.cursor_constrain ()
+ def cursor_forward (self,count=1): # <ESC>[{COUNT}C
+ self.cur_c = self.cur_c + count
+ self.cursor_constrain ()
+ def cursor_up (self,count=1): # <ESC>[{COUNT}A
+ self.cur_r = self.cur_r - count
+ self.cursor_constrain ()
+ def cursor_force_position (self, r, c): # <ESC>[{ROW};{COLUMN}f
+ '''Identical to Cursor Home.'''
+ self.cursor_home (r, c)
+ def cursor_save (self): # <ESC>[s
+ '''Save current cursor position.'''
+ pass
+ def cursor_unsave (self): # <ESC>[u
+ '''Restores cursor position after a Save Cursor.'''
+ pass
+ def cursor_save_attrs (self): # <ESC>7
+ '''Save current cursor position.'''
+ pass
+ def cursor_restore_attrs (self): # <ESC>8
+ '''Restores cursor position after a Save Cursor.'''
+ pass
+ def scroll_constrain (self):
+ '''This keeps the scroll region within the screen region.'''
+ if self.scroll_row_start <= 0:
+ self.scroll_row_start = 1
+ if self.scroll_row_end > self.rows:
+ self.scroll_row_end = self.rows
+ def scroll_screen (self): # <ESC>[r
+ '''Enable scrolling for entire display.'''
+ self.scroll_row_start = 1
+ self.scroll_row_end = self.rows
+ def scroll_screen_rows (self, rs, re): # <ESC>[{start};{end}r
+ '''Enable scrolling from row {start} to row {end}.'''
+ self.scroll_row_start = rs
+ self.scroll_row_end = re
+ self.scroll_constrain()
+ def scroll_down (self): # <ESC>D
+ '''Scroll display down one line.'''
+ # Screen is indexed from 1, but arrays are indexed from 0.
+ s = self.scroll_row_start - 1
+ e = self.scroll_row_end - 1
+ self.w[s+1:e+1] = copy.deepcopy(self.w[s:e])
+ def scroll_up (self): # <ESC>M
+ '''Scroll display up one line.'''
+ # Screen is indexed from 1, but arrays are indexed from 0.
+ s = self.scroll_row_start - 1
+ e = self.scroll_row_end - 1
+ self.w[s:e] = copy.deepcopy(self.w[s+1:e+1])
+ def erase_end_of_line (self): # <ESC>[K
+ '''Erases from the current cursor position to
+ the end of the current line.'''
+ self.fill_region (self.cur_r, self.cur_c, self.cur_r, self.cols)
+ def erase_start_of_line (self): # <ESC>[1K
+ '''Erases from the current cursor position to
+ the start of the current line.'''
+ self.fill_region (self.cur_r, 1, self.cur_r, self.cur_c)
+ def erase_line (self): # <ESC>[2K
+ '''Erases the entire current line.'''
+ self.fill_region (self.cur_r, 1, self.cur_r, self.cols)
+ def erase_down (self): # <ESC>[J
+ '''Erases the screen from the current line down to
+ the bottom of the screen.'''
+ self.erase_end_of_line ()
+ self.fill_region (self.cur_r + 1, 1, self.rows, self.cols)
+ def erase_up (self): # <ESC>[1J
+ '''Erases the screen from the current line up to
+ the top of the screen.'''
+ self.erase_start_of_line ()
+ self.fill_region (self.cur_r-1, 1, 1, self.cols)
+ def erase_screen (self): # <ESC>[2J
+ '''Erases the screen with the background color.'''
+ self.fill ()
+
+ def set_tab (self): # <ESC>H
+ '''Sets a tab at the current position.'''
+ pass
+ def clear_tab (self): # <ESC>[g
+ '''Clears tab at the current position.'''
+ pass
+ def clear_all_tabs (self): # <ESC>[3g
+ '''Clears all tabs.'''
+ pass
+
+# Insert line Esc [ Pn L
+# Delete line Esc [ Pn M
+# Delete character Esc [ Pn P
+# Scrolling region Esc [ Pn(top);Pn(bot) r
+
+
+
+
+
+import tty, termios, sys
+
+def getkey():
+ file = sys.stdin.fileno()
+ mode = termios.tcgetattr(file)
+ try:
+ tty.setraw(file, termios.TCSANOW)
+ ch = sys.stdin.read(1)
+ finally:
+ termios.tcsetattr(file, termios.TCSANOW, mode)
+ return ch
+
+
+def test_typing ():
+ s = screen (10,10)
+ while 1:
+ ch = getkey()
+ s.type(ch)
+ print str(s)
+ print
+
+#s = screen ()
+#s.fill ('X')
+#print s.w
+import sys
+
+e = chr(0x1b)
+#sys.stdout.write (e+'[6n')
+#sys.stdout.write (e+'[c')
+#sys.stdout.write (e+'[0c')
+#sys.stdout.write (e+'[5;10r')
+#sys.stdout.write (e+'[r')
+#sys.stdout.write (e+'[5;10H')
+#sys.stdout.write (e+'[K')
+#sys.stdout.write (e+'[6n')
+#for i in range (0,9):
+# sys.stdout.write (e + 'D')
+
+s = screen (10,10)
+s.fill ('X')
+s.fill_region (2,2,9,9,'O')
+s.fill_region (5,5,s.rows-5,s.cols-5, 'X')
+for r in range (1,s.rows + 1):
+ s.put (r, 1, str(r))
+s.put(1,1, '1')
+s.put(1,s.cols, 'C')
+s.put(s.rows, 1, 'R')
+s.put(s.rows, s.cols, '*')
+from pprint import *
+print
+pprint (s.w)
+print str(s)
+print
+s.scroll_screen_rows (4,6)
+s.scroll_down()
+s.scroll_down()
+s.scroll_down()
+print str(s)
+print
+s.scroll_screen_rows (3,7)
+s.scroll_up()
+s.scroll_up()
+s.scroll_up()
+s.scroll_up()
+s.scroll_up()
+print str(s)
+s.fill('.')
+s.cursor_home()
+for r in range (1,11):
+ for c in range(1,11):
+ s.type(r*c)
+print
+print str(s)
+
+#test_typing()