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