Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / conch / ui / ansi.py
1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
3
4 #
5 """Module to parse ANSI escape sequences
6
7 Maintainer: Jean-Paul Calderone
8 """
9
10 import string
11
12 # Twisted imports
13 from twisted.python import log
14
15 class ColorText:
16     """
17     Represents an element of text along with the texts colors and
18     additional attributes.
19     """
20
21     # The colors to use
22     COLORS = ('b', 'r', 'g', 'y', 'l', 'm', 'c', 'w')
23     BOLD_COLORS = tuple([x.upper() for x in COLORS])
24     BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(len(COLORS))
25
26     # Color names
27     COLOR_NAMES = (
28         'Black', 'Red', 'Green', 'Yellow', 'Blue', 'Magenta', 'Cyan', 'White'
29     )
30
31     def __init__(self, text, fg, bg, display, bold, underline, flash, reverse):
32         self.text, self.fg, self.bg = text, fg, bg
33         self.display = display
34         self.bold = bold
35         self.underline = underline
36         self.flash = flash
37         self.reverse = reverse
38         if self.reverse:
39             self.fg, self.bg = self.bg, self.fg
40
41
42 class AnsiParser:
43     """
44     Parser class for ANSI codes.
45     """
46
47     # Terminators for cursor movement ansi controls - unsupported
48     CURSOR_SET = ('H', 'f', 'A', 'B', 'C', 'D', 'R', 's', 'u', 'd','G')
49
50     # Terminators for erasure ansi controls - unsupported
51     ERASE_SET = ('J', 'K', 'P')
52     
53     # Terminators for mode change ansi controls - unsupported
54     MODE_SET = ('h', 'l')
55     
56     # Terminators for keyboard assignment ansi controls - unsupported
57     ASSIGN_SET = ('p',)
58     
59     # Terminators for color change ansi controls - supported
60     COLOR_SET = ('m',)
61
62     SETS = (CURSOR_SET, ERASE_SET, MODE_SET, ASSIGN_SET, COLOR_SET)
63
64     def __init__(self, defaultFG, defaultBG):
65         self.defaultFG, self.defaultBG = defaultFG, defaultBG
66         self.currentFG, self.currentBG = self.defaultFG, self.defaultBG
67         self.bold, self.flash, self.underline, self.reverse = 0, 0, 0, 0
68         self.display = 1
69         self.prepend = ''
70
71     
72     def stripEscapes(self, string):
73         """
74         Remove all ANSI color escapes from the given string.
75         """
76         result = ''
77         show = 1
78         i = 0
79         L = len(string)
80         while i < L:
81             if show == 0 and string[i] in _sets:
82                 show = 1
83             elif show:
84                 n = string.find('\x1B', i)
85                 if n == -1:
86                     return result + string[i:]
87                 else:
88                     result = result + string[i:n]
89                     i = n
90                     show = 0
91             i = i + 1
92         return result
93
94     def writeString(self, colorstr):
95         pass
96
97     def parseString(self, str):
98         """
99         Turn a string input into a list of L{ColorText} elements.
100         """
101
102         if self.prepend:
103             str = self.prepend + str
104             self.prepend = ''
105         parts = str.split('\x1B')
106         
107         if len(parts) == 1:
108             self.writeString(self.formatText(parts[0]))
109         else:
110             self.writeString(self.formatText(parts[0]))
111             for s in parts[1:]:
112                 L = len(s)
113                 i = 0 
114                 type = None
115                 while i < L:
116                     if s[i] not in string.digits+'[;?':
117                         break
118                     i+=1
119                 if not s:
120                     self.prepend = '\x1b'
121                     return
122                 if s[0]!='[':
123                     self.writeString(self.formatText(s[i+1:]))
124                     continue
125                 else:
126                     s=s[1:]
127                     i-=1
128                 if i==L-1:
129                     self.prepend = '\x1b['
130                     return
131                 type = _setmap.get(s[i], None)
132                 if type is None:
133                     continue 
134
135                 if type == AnsiParser.COLOR_SET:
136                     self.parseColor(s[:i + 1])
137                     s = s[i + 1:]
138                     self.writeString(self.formatText(s))
139                 elif type == AnsiParser.CURSOR_SET:
140                     cursor, s = s[:i+1], s[i+1:]
141                     self.parseCursor(cursor)
142                     self.writeString(self.formatText(s))
143                 elif type == AnsiParser.ERASE_SET:
144                     erase, s = s[:i+1], s[i+1:]
145                     self.parseErase(erase)
146                     self.writeString(self.formatText(s))
147                 elif type == AnsiParser.MODE_SET:
148                     mode, s = s[:i+1], s[i+1:]
149                     #self.parseErase('2J')
150                     self.writeString(self.formatText(s))
151                 elif i == L:
152                     self.prepend = '\x1B[' + s
153                 else:
154                     log.msg('Unhandled ANSI control type: %c' % (s[i],))
155                     s = s[i + 1:]
156                     self.writeString(self.formatText(s))
157
158     def parseColor(self, str):
159         """
160         Handle a single ANSI color sequence
161         """
162         # Drop the trailing 'm'
163         str = str[:-1]
164
165         if not str:
166             str = '0'
167
168         try:
169             parts = map(int, str.split(';'))
170         except ValueError:
171             log.msg('Invalid ANSI color sequence (%d): %s' % (len(str), str))
172             self.currentFG, self.currentBG = self.defaultFG, self.defaultBG
173             return
174
175         for x in parts:
176             if x == 0:
177                 self.currentFG, self.currentBG = self.defaultFG, self.defaultBG
178                 self.bold, self.flash, self.underline, self.reverse = 0, 0, 0, 0
179                 self.display = 1
180             elif x == 1:
181                 self.bold = 1
182             elif 30 <= x <= 37:
183                 self.currentFG = x - 30
184             elif 40 <= x <= 47:
185                 self.currentBG = x - 40
186             elif x == 39:
187                 self.currentFG = self.defaultFG
188             elif x == 49:
189                 self.currentBG = self.defaultBG
190             elif x == 4:
191                 self.underline = 1
192             elif x == 5:
193                 self.flash = 1
194             elif x == 7:
195                 self.reverse = 1
196             elif x == 8:
197                 self.display = 0
198             elif x == 22:
199                 self.bold = 0
200             elif x == 24:
201                 self.underline = 0
202             elif x == 25:
203                 self.blink = 0
204             elif x == 27:
205                 self.reverse = 0
206             elif x == 28:
207                 self.display = 1
208             else:
209                 log.msg('Unrecognised ANSI color command: %d' % (x,))
210
211     def parseCursor(self, cursor):
212         pass
213
214     def parseErase(self, erase):
215         pass
216
217
218     def pickColor(self, value, mode, BOLD = ColorText.BOLD_COLORS):
219         if mode:
220             return ColorText.COLORS[value]
221         else:
222             return self.bold and BOLD[value] or ColorText.COLORS[value]
223
224
225     def formatText(self, text):
226         return ColorText(
227             text,
228             self.pickColor(self.currentFG, 0),
229             self.pickColor(self.currentBG, 1),
230             self.display, self.bold, self.underline, self.flash, self.reverse
231         )
232
233
234 _sets = ''.join(map(''.join, AnsiParser.SETS))
235
236 _setmap = {}
237 for s in AnsiParser.SETS:
238     for r in s:
239         _setmap[r] = s
240 del s