implemented working externAgent wrapper
[progcomp10.git] / src / link / pexpect / ANSI.py
1 """This implements an ANSI terminal emulator as a subclass of screen.
2
3 $Id: ANSI.py 491 2007-12-16 20:04:57Z noah $
4 """
5 # references:
6 #     http://www.retards.org/terminals/vt102.html
7 #     http://vt100.net/docs/vt102-ug/contents.html
8 #     http://vt100.net/docs/vt220-rm/
9 #     http://www.termsys.demon.co.uk/vtansi.htm
10
11 import screen
12 import FSM
13 import copy
14 import string
15
16 def Emit (fsm):
17
18     screen = fsm.memory[0]
19     screen.write_ch(fsm.input_symbol)
20
21 def StartNumber (fsm):
22
23     fsm.memory.append (fsm.input_symbol)
24
25 def BuildNumber (fsm):
26
27     ns = fsm.memory.pop()
28     ns = ns + fsm.input_symbol
29     fsm.memory.append (ns)
30
31 def DoBackOne (fsm):
32
33     screen = fsm.memory[0]
34     screen.cursor_back ()
35
36 def DoBack (fsm):
37
38     count = int(fsm.memory.pop())
39     screen = fsm.memory[0]
40     screen.cursor_back (count)
41
42 def DoDownOne (fsm):
43
44     screen = fsm.memory[0]
45     screen.cursor_down ()
46
47 def DoDown (fsm):
48
49     count = int(fsm.memory.pop())
50     screen = fsm.memory[0]
51     screen.cursor_down (count)
52
53 def DoForwardOne (fsm):
54
55     screen = fsm.memory[0]
56     screen.cursor_forward ()
57
58 def DoForward (fsm):
59
60     count = int(fsm.memory.pop())
61     screen = fsm.memory[0]
62     screen.cursor_forward (count)
63
64 def DoUpReverse (fsm):
65
66     screen = fsm.memory[0]
67     screen.cursor_up_reverse()
68
69 def DoUpOne (fsm):
70
71     screen = fsm.memory[0]
72     screen.cursor_up ()
73
74 def DoUp (fsm):
75
76     count = int(fsm.memory.pop())
77     screen = fsm.memory[0]
78     screen.cursor_up (count)
79
80 def DoHome (fsm):
81
82     c = int(fsm.memory.pop())
83     r = int(fsm.memory.pop())
84     screen = fsm.memory[0]
85     screen.cursor_home (r,c)
86
87 def DoHomeOrigin (fsm):
88
89     c = 1
90     r = 1
91     screen = fsm.memory[0]
92     screen.cursor_home (r,c)
93
94 def DoEraseDown (fsm):
95
96     screen = fsm.memory[0]
97     screen.erase_down()
98
99 def DoErase (fsm):
100
101     arg = int(fsm.memory.pop())
102     screen = fsm.memory[0]
103     if arg == 0:
104         screen.erase_down()
105     elif arg == 1:
106         screen.erase_up()
107     elif arg == 2:
108         screen.erase_screen()
109
110 def DoEraseEndOfLine (fsm):
111
112     screen = fsm.memory[0]
113     screen.erase_end_of_line()
114
115 def DoEraseLine (fsm):
116
117     screen = fsm.memory[0]
118     if arg == 0:
119         screen.end_of_line()
120     elif arg == 1:
121         screen.start_of_line()
122     elif arg == 2:
123         screen.erase_line()
124
125 def DoEnableScroll (fsm):
126
127     screen = fsm.memory[0]
128     screen.scroll_screen()
129
130 def DoCursorSave (fsm):
131
132     screen = fsm.memory[0]
133     screen.cursor_save_attrs()
134
135 def DoCursorRestore (fsm):
136
137     screen = fsm.memory[0]
138     screen.cursor_restore_attrs()
139
140 def DoScrollRegion (fsm):
141
142     screen = fsm.memory[0]
143     r2 = int(fsm.memory.pop())
144     r1 = int(fsm.memory.pop())
145     screen.scroll_screen_rows (r1,r2)
146
147 def DoMode (fsm):
148
149     screen = fsm.memory[0]
150     mode = fsm.memory.pop() # Should be 4
151     # screen.setReplaceMode ()
152
153 def Log (fsm):
154
155     screen = fsm.memory[0]
156     fsm.memory = [screen]
157     fout = open ('log', 'a')
158     fout.write (fsm.input_symbol + ',' + fsm.current_state + '\n')
159     fout.close()
160
161 class term (screen.screen):
162     """This is a placeholder. 
163     In theory I might want to add other terminal types.
164     """
165     def __init__ (self, r=24, c=80):
166         screen.screen.__init__(self, r,c)
167
168 class ANSI (term):
169
170     """This class encapsulates a generic terminal. It filters a stream and
171     maintains the state of a screen object. """
172
173     def __init__ (self, r=24,c=80):
174
175         term.__init__(self,r,c)
176
177         #self.screen = screen (24,80)
178         self.state = FSM.FSM ('INIT',[self])
179         self.state.set_default_transition (Log, 'INIT')
180         self.state.add_transition_any ('INIT', Emit, 'INIT')
181         self.state.add_transition ('\x1b', 'INIT', None, 'ESC')
182         self.state.add_transition_any ('ESC', Log, 'INIT')
183         self.state.add_transition ('(', 'ESC', None, 'G0SCS')
184         self.state.add_transition (')', 'ESC', None, 'G1SCS')
185         self.state.add_transition_list ('AB012', 'G0SCS', None, 'INIT')
186         self.state.add_transition_list ('AB012', 'G1SCS', None, 'INIT')
187         self.state.add_transition ('7', 'ESC', DoCursorSave, 'INIT')
188         self.state.add_transition ('8', 'ESC', DoCursorRestore, 'INIT')
189         self.state.add_transition ('M', 'ESC', DoUpReverse, 'INIT')
190         self.state.add_transition ('>', 'ESC', DoUpReverse, 'INIT')
191         self.state.add_transition ('<', 'ESC', DoUpReverse, 'INIT')
192         self.state.add_transition ('=', 'ESC', None, 'INIT') # Selects application keypad.
193         self.state.add_transition ('#', 'ESC', None, 'GRAPHICS_POUND')
194         self.state.add_transition_any ('GRAPHICS_POUND', None, 'INIT')
195         self.state.add_transition ('[', 'ESC', None, 'ELB')
196         # ELB means Escape Left Bracket. That is ^[[
197         self.state.add_transition ('H', 'ELB', DoHomeOrigin, 'INIT')
198         self.state.add_transition ('D', 'ELB', DoBackOne, 'INIT')
199         self.state.add_transition ('B', 'ELB', DoDownOne, 'INIT')
200         self.state.add_transition ('C', 'ELB', DoForwardOne, 'INIT')
201         self.state.add_transition ('A', 'ELB', DoUpOne, 'INIT')
202         self.state.add_transition ('J', 'ELB', DoEraseDown, 'INIT')
203         self.state.add_transition ('K', 'ELB', DoEraseEndOfLine, 'INIT')
204         self.state.add_transition ('r', 'ELB', DoEnableScroll, 'INIT')
205         self.state.add_transition ('m', 'ELB', None, 'INIT')
206         self.state.add_transition ('?', 'ELB', None, 'MODECRAP')
207         self.state.add_transition_list (string.digits, 'ELB', StartNumber, 'NUMBER_1')
208         self.state.add_transition_list (string.digits, 'NUMBER_1', BuildNumber, 'NUMBER_1')
209         self.state.add_transition ('D', 'NUMBER_1', DoBack, 'INIT')
210         self.state.add_transition ('B', 'NUMBER_1', DoDown, 'INIT')
211         self.state.add_transition ('C', 'NUMBER_1', DoForward, 'INIT')
212         self.state.add_transition ('A', 'NUMBER_1', DoUp, 'INIT')
213         self.state.add_transition ('J', 'NUMBER_1', DoErase, 'INIT')
214         self.state.add_transition ('K', 'NUMBER_1', DoEraseLine, 'INIT')
215         self.state.add_transition ('l', 'NUMBER_1', DoMode, 'INIT')
216         ### It gets worse... the 'm' code can have infinite number of
217         ### number;number;number before it. I've never seen more than two,
218         ### but the specs say it's allowed. crap!
219         self.state.add_transition ('m', 'NUMBER_1', None, 'INIT')
220         ### LED control. Same problem as 'm' code.
221         self.state.add_transition ('q', 'NUMBER_1', None, 'INIT') 
222         
223         # \E[?47h appears to be "switch to alternate screen"
224         # \E[?47l restores alternate screen... I think.
225         self.state.add_transition_list (string.digits, 'MODECRAP', StartNumber, 'MODECRAP_NUM')
226         self.state.add_transition_list (string.digits, 'MODECRAP_NUM', BuildNumber, 'MODECRAP_NUM')
227         self.state.add_transition ('l', 'MODECRAP_NUM', None, 'INIT')
228         self.state.add_transition ('h', 'MODECRAP_NUM', None, 'INIT')
229
230 #RM   Reset Mode                Esc [ Ps l                   none
231         self.state.add_transition (';', 'NUMBER_1', None, 'SEMICOLON')
232         self.state.add_transition_any ('SEMICOLON', Log, 'INIT')
233         self.state.add_transition_list (string.digits, 'SEMICOLON', StartNumber, 'NUMBER_2')
234         self.state.add_transition_list (string.digits, 'NUMBER_2', BuildNumber, 'NUMBER_2')
235         self.state.add_transition_any ('NUMBER_2', Log, 'INIT')
236         self.state.add_transition ('H', 'NUMBER_2', DoHome, 'INIT')
237         self.state.add_transition ('f', 'NUMBER_2', DoHome, 'INIT')
238         self.state.add_transition ('r', 'NUMBER_2', DoScrollRegion, 'INIT')
239         ### It gets worse... the 'm' code can have infinite number of
240         ### number;number;number before it. I've never seen more than two,
241         ### but the specs say it's allowed. crap!
242         self.state.add_transition ('m', 'NUMBER_2', None, 'INIT')
243         ### LED control. Same problem as 'm' code.
244         self.state.add_transition ('q', 'NUMBER_2', None, 'INIT') 
245
246     def process (self, c):
247
248         self.state.process(c)
249
250     def process_list (self, l):
251
252         self.write(l)
253
254     def write (self, s):
255
256         for c in s:
257             self.process(c)
258
259     def flush (self):
260
261         pass
262
263     def write_ch (self, ch):
264
265         """This puts a character at the current cursor position. cursor
266         position if moved forward with wrap-around, but no scrolling is done if
267         the cursor hits the lower-right corner of the screen. """
268
269         #\r and \n both produce a call to crlf().
270         ch = ch[0]
271
272         if ch == '\r':
273         #    self.crlf()
274             return
275         if ch == '\n':
276             self.crlf()
277             return
278         if ch == chr(screen.BS):
279             self.cursor_back()
280             self.put_abs(self.cur_r, self.cur_c, ' ')
281             return
282
283         if ch not in string.printable:
284             fout = open ('log', 'a')
285             fout.write ('Nonprint: ' + str(ord(ch)) + '\n')
286             fout.close()
287             return
288         self.put_abs(self.cur_r, self.cur_c, ch)
289         old_r = self.cur_r
290         old_c = self.cur_c
291         self.cursor_forward()
292         if old_c == self.cur_c:
293             self.cursor_down()
294             if old_r != self.cur_r:
295                 self.cursor_home (self.cur_r, 1)
296             else:
297                 self.scroll_up ()
298                 self.cursor_home (self.cur_r, 1)
299                 self.erase_line()
300
301 #    def test (self):
302 #
303 #        import sys
304 #        write_text = 'I\'ve got a ferret sticking up my nose.\n' + \
305 #        '(He\'s got a ferret sticking up his nose.)\n' + \
306 #        'How it got there I can\'t tell\n' + \
307 #        'But now it\'s there it hurts like hell\n' + \
308 #        'And what is more it radically affects my sense of smell.\n' + \
309 #        '(His sense of smell.)\n' + \
310 #        'I can see a bare-bottomed mandril.\n' + \
311 #        '(Slyly eyeing his other nostril.)\n' + \
312 #        'If it jumps inside there too I really don\'t know what to do\n' + \
313 #        'I\'ll be the proud posessor of a kind of nasal zoo.\n' + \
314 #        '(A nasal zoo.)\n' + \
315 #        'I\'ve got a ferret sticking up my nose.\n' + \
316 #        '(And what is worst of all it constantly explodes.)\n' + \
317 #        '"Ferrets don\'t explode," you say\n' + \
318 #        'But it happened nine times yesterday\n' + \
319 #        'And I should know for each time I was standing in the way.\n' + \
320 #        'I\'ve got a ferret sticking up my nose.\n' + \
321 #        '(He\'s got a ferret sticking up his nose.)\n' + \
322 #        'How it got there I can\'t tell\n' + \
323 #        'But now it\'s there it hurts like hell\n' + \
324 #        'And what is more it radically affects my sense of smell.\n' + \
325 #        '(His sense of smell.)'
326 #        self.fill('.')
327 #        self.cursor_home()
328 #        for c in write_text:
329 #            self.write_ch (c)
330 #        print str(self)
331 #
332 #if __name__ == '__main__':
333 #    t = ANSI(6,65)
334 #    t.test()

UCC git Repository :: git.ucc.asn.au