third_party:waf: update to upstream 2.0.4 release
[amitay/samba.git] / third_party / waf / waflib / ansiterm.py
1 #! /usr/bin/env python
2 # encoding: utf-8
3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
4
5 #!/usr/bin/env python
6 # encoding: utf-8
7
8 """
9 Emulate a vt100 terminal in cmd.exe
10
11 By wrapping sys.stdout / sys.stderr with Ansiterm,
12 the vt100 escape characters will be interpreted and
13 the equivalent actions will be performed with Win32
14 console commands.
15
16 """
17
18 import os, re, sys
19 from waflib import Utils
20
21 wlock = Utils.threading.Lock()
22
23 try:
24         from ctypes import Structure, windll, c_short, c_ushort, c_ulong, c_int, byref, c_wchar, POINTER, c_long
25 except ImportError:
26
27         class AnsiTerm(object):
28                 def __init__(self, stream):
29                         self.stream = stream
30                         try:
31                                 self.errors = self.stream.errors
32                         except AttributeError:
33                                 pass # python 2.5
34                         self.encoding = self.stream.encoding
35
36                 def write(self, txt):
37                         try:
38                                 wlock.acquire()
39                                 self.stream.write(txt)
40                                 self.stream.flush()
41                         finally:
42                                 wlock.release()
43
44                 def fileno(self):
45                         return self.stream.fileno()
46
47                 def flush(self):
48                         self.stream.flush()
49
50                 def isatty(self):
51                         return self.stream.isatty()
52 else:
53
54         class COORD(Structure):
55                 _fields_ = [("X", c_short), ("Y", c_short)]
56
57         class SMALL_RECT(Structure):
58                 _fields_ = [("Left", c_short), ("Top", c_short), ("Right", c_short), ("Bottom", c_short)]
59
60         class CONSOLE_SCREEN_BUFFER_INFO(Structure):
61                 _fields_ = [("Size", COORD), ("CursorPosition", COORD), ("Attributes", c_ushort), ("Window", SMALL_RECT), ("MaximumWindowSize", COORD)]
62
63         class CONSOLE_CURSOR_INFO(Structure):
64                 _fields_ = [('dwSize', c_ulong), ('bVisible', c_int)]
65
66         try:
67                 _type = unicode
68         except NameError:
69                 _type = str
70
71         to_int = lambda number, default: number and int(number) or default
72
73         STD_OUTPUT_HANDLE = -11
74         STD_ERROR_HANDLE = -12
75
76         windll.kernel32.GetStdHandle.argtypes = [c_ulong]
77         windll.kernel32.GetStdHandle.restype = c_ulong
78         windll.kernel32.GetConsoleScreenBufferInfo.argtypes = [c_ulong, POINTER(CONSOLE_SCREEN_BUFFER_INFO)]
79         windll.kernel32.GetConsoleScreenBufferInfo.restype = c_long
80         windll.kernel32.SetConsoleTextAttribute.argtypes = [c_ulong, c_ushort]
81         windll.kernel32.SetConsoleTextAttribute.restype = c_long
82         windll.kernel32.FillConsoleOutputCharacterW.argtypes = [c_ulong, c_wchar, c_ulong, POINTER(COORD), POINTER(c_ulong)]
83         windll.kernel32.FillConsoleOutputCharacterW.restype = c_long
84         windll.kernel32.FillConsoleOutputAttribute.argtypes = [c_ulong, c_ushort, c_ulong, POINTER(COORD), POINTER(c_ulong) ]
85         windll.kernel32.FillConsoleOutputAttribute.restype = c_long
86         windll.kernel32.SetConsoleCursorPosition.argtypes = [c_ulong, POINTER(COORD) ]
87         windll.kernel32.SetConsoleCursorPosition.restype = c_long
88         windll.kernel32.SetConsoleCursorInfo.argtypes = [c_ulong, POINTER(CONSOLE_CURSOR_INFO)]
89         windll.kernel32.SetConsoleCursorInfo.restype = c_long
90
91         class AnsiTerm(object):
92                 """
93                 emulate a vt100 terminal in cmd.exe
94                 """
95                 def __init__(self, s):
96                         self.stream = s
97                         try:
98                                 self.errors = s.errors
99                         except AttributeError:
100                                 pass # python2.5
101                         self.encoding = s.encoding
102                         self.cursor_history = []
103
104                         handle = (s.fileno() == 2) and STD_ERROR_HANDLE or STD_OUTPUT_HANDLE
105                         self.hconsole = windll.kernel32.GetStdHandle(handle)
106
107                         self._sbinfo = CONSOLE_SCREEN_BUFFER_INFO()
108
109                         self._csinfo = CONSOLE_CURSOR_INFO()
110                         windll.kernel32.GetConsoleCursorInfo(self.hconsole, byref(self._csinfo))
111
112                         # just to double check that the console is usable
113                         self._orig_sbinfo = CONSOLE_SCREEN_BUFFER_INFO()
114                         r = windll.kernel32.GetConsoleScreenBufferInfo(self.hconsole, byref(self._orig_sbinfo))
115                         self._isatty = r == 1
116
117                 def screen_buffer_info(self):
118                         """
119                         Updates self._sbinfo and returns it
120                         """
121                         windll.kernel32.GetConsoleScreenBufferInfo(self.hconsole, byref(self._sbinfo))
122                         return self._sbinfo
123
124                 def clear_line(self, param):
125                         mode = param and int(param) or 0
126                         sbinfo = self.screen_buffer_info()
127                         if mode == 1: # Clear from beginning of line to cursor position
128                                 line_start = COORD(0, sbinfo.CursorPosition.Y)
129                                 line_length = sbinfo.Size.X
130                         elif mode == 2: # Clear entire line
131                                 line_start = COORD(sbinfo.CursorPosition.X, sbinfo.CursorPosition.Y)
132                                 line_length = sbinfo.Size.X - sbinfo.CursorPosition.X
133                         else: # Clear from cursor position to end of line
134                                 line_start = sbinfo.CursorPosition
135                                 line_length = sbinfo.Size.X - sbinfo.CursorPosition.X
136                         chars_written = c_ulong()
137                         windll.kernel32.FillConsoleOutputCharacterW(self.hconsole, c_wchar(' '), line_length, line_start, byref(chars_written))
138                         windll.kernel32.FillConsoleOutputAttribute(self.hconsole, sbinfo.Attributes, line_length, line_start, byref(chars_written))
139
140                 def clear_screen(self, param):
141                         mode = to_int(param, 0)
142                         sbinfo = self.screen_buffer_info()
143                         if mode == 1: # Clear from beginning of screen to cursor position
144                                 clear_start = COORD(0, 0)
145                                 clear_length = sbinfo.CursorPosition.X * sbinfo.CursorPosition.Y
146                         elif mode == 2: # Clear entire screen and return cursor to home
147                                 clear_start = COORD(0, 0)
148                                 clear_length = sbinfo.Size.X * sbinfo.Size.Y
149                                 windll.kernel32.SetConsoleCursorPosition(self.hconsole, clear_start)
150                         else: # Clear from cursor position to end of screen
151                                 clear_start = sbinfo.CursorPosition
152                                 clear_length = ((sbinfo.Size.X - sbinfo.CursorPosition.X) + sbinfo.Size.X * (sbinfo.Size.Y - sbinfo.CursorPosition.Y))
153                         chars_written = c_ulong()
154                         windll.kernel32.FillConsoleOutputCharacterW(self.hconsole, c_wchar(' '), clear_length, clear_start, byref(chars_written))
155                         windll.kernel32.FillConsoleOutputAttribute(self.hconsole, sbinfo.Attributes, clear_length, clear_start, byref(chars_written))
156
157                 def push_cursor(self, param):
158                         sbinfo = self.screen_buffer_info()
159                         self.cursor_history.append(sbinfo.CursorPosition)
160
161                 def pop_cursor(self, param):
162                         if self.cursor_history:
163                                 old_pos = self.cursor_history.pop()
164                                 windll.kernel32.SetConsoleCursorPosition(self.hconsole, old_pos)
165
166                 def set_cursor(self, param):
167                         y, sep, x = param.partition(';')
168                         x = to_int(x, 1) - 1
169                         y = to_int(y, 1) - 1
170                         sbinfo = self.screen_buffer_info()
171                         new_pos = COORD(
172                                 min(max(0, x), sbinfo.Size.X),
173                                 min(max(0, y), sbinfo.Size.Y)
174                         )
175                         windll.kernel32.SetConsoleCursorPosition(self.hconsole, new_pos)
176
177                 def set_column(self, param):
178                         x = to_int(param, 1) - 1
179                         sbinfo = self.screen_buffer_info()
180                         new_pos = COORD(
181                                 min(max(0, x), sbinfo.Size.X),
182                                 sbinfo.CursorPosition.Y
183                         )
184                         windll.kernel32.SetConsoleCursorPosition(self.hconsole, new_pos)
185
186                 def move_cursor(self, x_offset=0, y_offset=0):
187                         sbinfo = self.screen_buffer_info()
188                         new_pos = COORD(
189                                 min(max(0, sbinfo.CursorPosition.X + x_offset), sbinfo.Size.X),
190                                 min(max(0, sbinfo.CursorPosition.Y + y_offset), sbinfo.Size.Y)
191                         )
192                         windll.kernel32.SetConsoleCursorPosition(self.hconsole, new_pos)
193
194                 def move_up(self, param):
195                         self.move_cursor(y_offset = -to_int(param, 1))
196
197                 def move_down(self, param):
198                         self.move_cursor(y_offset = to_int(param, 1))
199
200                 def move_left(self, param):
201                         self.move_cursor(x_offset = -to_int(param, 1))
202
203                 def move_right(self, param):
204                         self.move_cursor(x_offset = to_int(param, 1))
205
206                 def next_line(self, param):
207                         sbinfo = self.screen_buffer_info()
208                         self.move_cursor(
209                                 x_offset = -sbinfo.CursorPosition.X,
210                                 y_offset = to_int(param, 1)
211                         )
212
213                 def prev_line(self, param):
214                         sbinfo = self.screen_buffer_info()
215                         self.move_cursor(
216                                 x_offset = -sbinfo.CursorPosition.X,
217                                 y_offset = -to_int(param, 1)
218                         )
219
220                 def rgb2bgr(self, c):
221                         return ((c&1) << 2) | (c&2) | ((c&4)>>2)
222
223                 def set_color(self, param):
224                         cols = param.split(';')
225                         sbinfo = self.screen_buffer_info()
226                         attr = sbinfo.Attributes
227                         for c in cols:
228                                 c = to_int(c, 0)
229                                 if 29 < c < 38: # fgcolor
230                                         attr = (attr & 0xfff0) | self.rgb2bgr(c - 30)
231                                 elif 39 < c < 48: # bgcolor
232                                         attr = (attr & 0xff0f) | (self.rgb2bgr(c - 40) << 4)
233                                 elif c == 0: # reset
234                                         attr = self._orig_sbinfo.Attributes
235                                 elif c == 1: # strong
236                                         attr |= 0x08
237                                 elif c == 4: # blink not available -> bg intensity
238                                         attr |= 0x80
239                                 elif c == 7: # negative
240                                         attr = (attr & 0xff88) | ((attr & 0x70) >> 4) | ((attr & 0x07) << 4)
241
242                         windll.kernel32.SetConsoleTextAttribute(self.hconsole, attr)
243
244                 def show_cursor(self,param):
245                         self._csinfo.bVisible = 1
246                         windll.kernel32.SetConsoleCursorInfo(self.hconsole, byref(self._csinfo))
247
248                 def hide_cursor(self,param):
249                         self._csinfo.bVisible = 0
250                         windll.kernel32.SetConsoleCursorInfo(self.hconsole, byref(self._csinfo))
251
252                 ansi_command_table = {
253                         'A': move_up,
254                         'B': move_down,
255                         'C': move_right,
256                         'D': move_left,
257                         'E': next_line,
258                         'F': prev_line,
259                         'G': set_column,
260                         'H': set_cursor,
261                         'f': set_cursor,
262                         'J': clear_screen,
263                         'K': clear_line,
264                         'h': show_cursor,
265                         'l': hide_cursor,
266                         'm': set_color,
267                         's': push_cursor,
268                         'u': pop_cursor,
269                 }
270                 # Match either the escape sequence or text not containing escape sequence
271                 ansi_tokens = re.compile('(?:\x1b\[([0-9?;]*)([a-zA-Z])|([^\x1b]+))')
272                 def write(self, text):
273                         try:
274                                 wlock.acquire()
275                                 if self._isatty:
276                                         for param, cmd, txt in self.ansi_tokens.findall(text):
277                                                 if cmd:
278                                                         cmd_func = self.ansi_command_table.get(cmd)
279                                                         if cmd_func:
280                                                                 cmd_func(self, param)
281                                                 else:
282                                                         self.writeconsole(txt)
283                                 else:
284                                         # no support for colors in the console, just output the text:
285                                         # eclipse or msys may be able to interpret the escape sequences
286                                         self.stream.write(text)
287                         finally:
288                                 wlock.release()
289
290                 def writeconsole(self, txt):
291                         chars_written = c_ulong()
292                         writeconsole = windll.kernel32.WriteConsoleA
293                         if isinstance(txt, _type):
294                                 writeconsole = windll.kernel32.WriteConsoleW
295
296                         # MSDN says that there is a shared buffer of 64 KB for the console
297                         # writes. Attempt to not get ERROR_NOT_ENOUGH_MEMORY, see waf issue #746
298                         done = 0
299                         todo = len(txt)
300                         chunk = 32<<10
301                         while todo != 0:
302                                 doing = min(chunk, todo)
303                                 buf = txt[done:done+doing]
304                                 r = writeconsole(self.hconsole, buf, doing, byref(chars_written), None)
305                                 if r == 0:
306                                         chunk >>= 1
307                                         continue
308                                 done += doing
309                                 todo -= doing
310
311
312                 def fileno(self):
313                         return self.stream.fileno()
314
315                 def flush(self):
316                         pass
317
318                 def isatty(self):
319                         return self._isatty
320
321         if sys.stdout.isatty() or sys.stderr.isatty():
322                 handle = sys.stdout.isatty() and STD_OUTPUT_HANDLE or STD_ERROR_HANDLE
323                 console = windll.kernel32.GetStdHandle(handle)
324                 sbinfo = CONSOLE_SCREEN_BUFFER_INFO()
325                 def get_term_cols():
326                         windll.kernel32.GetConsoleScreenBufferInfo(console, byref(sbinfo))
327                         # Issue 1401 - the progress bar cannot reach the last character
328                         return sbinfo.Size.X - 1
329
330 # just try and see
331 try:
332         import struct, fcntl, termios
333 except ImportError:
334         pass
335 else:
336         if (sys.stdout.isatty() or sys.stderr.isatty()) and os.environ.get('TERM', '') not in ('dumb', 'emacs'):
337                 FD = sys.stdout.isatty() and sys.stdout.fileno() or sys.stderr.fileno()
338                 def fun():
339                         return struct.unpack("HHHH", fcntl.ioctl(FD, termios.TIOCGWINSZ, struct.pack("HHHH", 0, 0, 0, 0)))[1]
340                 try:
341                         fun()
342                 except Exception as e:
343                         pass
344                 else:
345                         get_term_cols = fun
346