xref: /xnu-8792.41.9/tools/lldbmacros/core/standard.py (revision 5c2921b07a2480ab43ec66f5b9e41cb872bc554f)
1from __future__ import absolute_import, print_function
2
3from builtins import object
4
5import getopt
6import os
7import sys
8import re
9
10class ArgumentError(Exception):
11    """ Exception class for raising errors in command arguments. The lldb_command framework will catch this
12        class of exceptions and print suitable error message to user.
13    """
14    def __init__(self, msg):
15        self.error_message = msg
16    def __str__(self):
17        return str(self.error_message)
18
19
20class RedirectStdStreams(object):
21    def __init__(self, stdout=None, stderr=None):
22        self._stdout = stdout or sys.stdout
23        self._stderr = stderr or sys.stderr
24
25    def __enter__(self):
26        self.old_stdout, self.old_stderr = sys.stdout, sys.stderr
27        self.old_stdout.flush(); self.old_stderr.flush()
28        sys.stdout, sys.stderr = self._stdout, self._stderr
29
30    def __exit__(self, exc_type, exc_value, traceback):
31        self._stdout.flush(); self._stderr.flush()
32        sys.stdout = self.old_stdout
33        sys.stderr = self.old_stderr
34
35class IndentScope(object):
36    def __init__(self, O):
37        self._O = O
38
39    def __enter__(self):
40        self._O._indent += '    '
41
42    def __exit__(self, exc_type, exc_value, traceback):
43        self._O._indent = self._O._indent[:-4]
44
45class HeaderScope(object):
46    def __init__(self, O, hdr, indent = False):
47        self._O = O
48        self._header = hdr
49        self._indent = indent
50
51    def __enter__(self):
52        self._oldHeader = self._O._header
53        self._oldLastHeader = self._O._lastHeader
54        self._O._header = self._header
55        self._O._lastHeader = None
56        if self._indent:
57            self._O._indent += '    '
58
59    def __exit__(self, exc_type, exc_value, traceback):
60        self._O._header = self._oldHeader
61        self._O._lastHeader = self._oldLastHeader
62        if self._indent:
63            self._O._indent = self._O._indent[:-4]
64
65class VT(object):
66    Black        = "\033[38;5;0m"
67    DarkRed      = "\033[38;5;1m"
68    DarkGreen    = "\033[38;5;2m"
69    Brown        = "\033[38;5;3m"
70    DarkBlue     = "\033[38;5;4m"
71    DarkMagenta  = "\033[38;5;5m"
72    DarkCyan     = "\033[38;5;6m"
73    Grey         = "\033[38;5;7m"
74
75    DarkGrey     = "\033[38;5;8m"
76    Red          = "\033[38;5;9m"
77    Green        = "\033[38;5;10m"
78    Yellow       = "\033[38;5;11m"
79    Blue         = "\033[38;5;12m"
80    Magenta      = "\033[38;5;13m"
81    Cyan         = "\033[38;5;14m"
82    White        = "\033[38;5;15m"
83
84    Default      = "\033[39m"
85
86    Bold         = "\033[1m"
87    EndBold      = "\033[22m"
88
89    Oblique      = "\033[3m"
90    EndOblique   = "\033[23m"
91
92    Underline    = "\033[4m"
93    EndUnderline = "\033[24m"
94
95    Reset        = "\033[0m"
96
97class NOVT(object):
98    def __getattribute__(self, *args):
99        return ""
100
101class CommandOutput(object):
102    """
103    An output handler for all commands. Use Output.print to direct all output of macro via the handler.
104    These arguments are passed after a "--". eg
105    (lldb) zprint -- -o /tmp/zprint.out.txt
106
107    Currently this provide capabilities
108    -h show help
109    -o path/to/filename
110       The output of this command execution will be saved to file. Parser information or errors will
111       not be sent to file though. eg /tmp/output.txt
112    -s filter_string
113       the "filter_string" param is parsed to python regex expression and each line of output
114       will be printed/saved only if it matches the expression.
115       The command header will not be filtered in any case.
116    -p <plugin_name>
117       Send the output of the command to plugin.
118    -v ...
119       Up verbosity
120    -c <always|never|auto>
121       configure color
122    """
123    def __init__(self, cmd_name, CommandResult=None, fhandle=None):
124        """ Create a new instance to handle command output.
125        params:
126                CommandResult : SBCommandReturnObject result param from lldb's command invocation.
127        """
128        self.fname=None
129        self.fhandle=fhandle
130        self.FILTER=False
131        self.pluginRequired = False
132        self.pluginName = None
133        self.cmd_name = cmd_name
134        self.resultObj = CommandResult
135        self.verbose_level = 0
136        self.target_cmd_args = []
137        self.target_cmd_options = {}
138        self.color = None
139        self.isatty = os.isatty(sys.__stdout__.fileno())
140        self._indent = ''
141        self._buffer = ''
142
143        self._header = None
144        self._lastHeader = None
145        self._line = 0
146
147    def _write(self, s):
148        if self.fhandle != None:
149            self.fhandle.write(self._indent + s + "\n")
150        else:
151            self.resultObj.AppendMessage(self._indent + s)
152        self._line += 1
153
154    def _doColor(self):
155        if self.color is True:
156            return True;
157        return self.color is None and self.isatty
158
159    def _needsHeader(self):
160        if self._header is None:
161            return False
162        if self._lastHeader is None:
163            return True
164        if not self.isatty:
165            return False
166        return self._line - self._lastHeader > 40
167
168    def indent(self):
169        return IndentScope(self)
170
171    def table(self, header, indent = False):
172        return HeaderScope(self, header, indent)
173
174    def format(self, s, *args, **kwargs):
175        if self._doColor():
176            kwargs['VT'] = VT
177        else:
178            kwargs['VT'] = NOVT()
179
180        return s.format(*args, **kwargs)
181
182    def error(self, s, *args, **kwargs):
183        print(self.format("{cmd.cmd_name}: {VT.Red}"+s+"{VT.Default}", cmd=self, *args, **kwargs))
184
185    def write(self, s):
186        """ Handler for all commands output. By default just print to stdout """
187
188        s = self._buffer + s
189
190        while s.find('\n') != -1:
191            l, s = s.split("\n", 1)
192            if self.FILTER:
193                if not self.reg.search(l):
194                    continue
195                if self._doColor():
196                    l = self.reg.sub(VT.Underline + r"\g<0>" + VT.EndUnderline, l);
197
198            if len(l) and self._needsHeader():
199                for hdr in self._header.split("\n"):
200                    self._write(self.format("{VT.Bold}{:s}{VT.EndBold}", hdr))
201                self._lastHeader = self._line
202
203            self._write(l)
204
205        self._buffer = s
206
207    def flush(self):
208        if self.fhandle != None:
209            self.fhandle.flush()
210
211    def __del__(self):
212        """ closes any open files. report on any errors """
213        if self.fhandle != None and self.fname != None:
214            self.fhandle.close()
215
216    def setOptions(self, cmdargs, cmdoptions =''):
217        """ parse the arguments passed to the command
218            param :
219                cmdargs => [] of <str> (typically args.split())
220                cmdoptions : str - string of command level options.
221                             These should be CAPITAL LETTER options only.
222        """
223        opts=()
224        args = cmdargs
225        cmdoptions = cmdoptions.upper()
226        try:
227            opts,args = getopt.gnu_getopt(args,'hvo:s:p:c:'+ cmdoptions,[])
228            self.target_cmd_args = args
229        except getopt.GetoptError as err:
230            raise ArgumentError(str(err))
231        #continue with processing
232        for o,a in opts :
233            if o == "-h":
234                # This is misuse of exception but 'self' has no info on doc string.
235                # The caller may handle exception and display appropriate info
236                raise ArgumentError("HELP")
237            if o == "-o" and len(a) > 0:
238                self.fname=os.path.normpath(os.path.expanduser(a.strip()))
239                self.fhandle=open(self.fname,"w")
240                print("saving results in file ",str(a))
241                self.fhandle.write("(lldb)%s %s \n" % (self.cmd_name, " ".join(cmdargs)))
242                self.isatty = os.isatty(self.fhandle.fileno())
243            elif o == "-s" and len(a) > 0:
244                self.reg = re.compile(a.strip(),re.MULTILINE|re.DOTALL)
245                self.FILTER=True
246                print("showing results for regex:",a.strip())
247            elif o == "-p" and len(a) > 0:
248                self.pluginRequired = True
249                self.pluginName = a.strip()
250                #print "passing output to " + a.strip()
251            elif o == "-v":
252                self.verbose_level += 1
253            elif o == "-c":
254                if a in ["always", '1']:
255                    self.color = True
256                elif a in ["never", '0']:
257                    self.color = False
258                else:
259                    self.color = None
260            else:
261                o = o.strip()
262                self.target_cmd_options[o] = a
263
264
265