Subversion Repositories freemyipod

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
396 farthen 1
#!/usr/bin/env python
2
#
3
#
4
#    Copyright 2010 TheSeven, benedikt93, Farthen
5
#
6
#
427 farthen 7
#    This file is part of emCORE.
396 farthen 8
#
427 farthen 9
#    emCORE is free software: you can redistribute it and/or
396 farthen 10
#    modify it under the terms of the GNU General Public License as
11
#    published by the Free Software Foundation, either version 2 of the
12
#    License, or (at your option) any later version.
13
#
427 farthen 14
#    emCORE is distributed in the hope that it will be useful,
396 farthen 15
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
16
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
#    See the GNU General Public License for more details.
18
#
19
#    You should have received a copy of the GNU General Public License
427 farthen 20
#    along with emCORE.  If not, see <http://www.gnu.org/licenses/>.
396 farthen 21
#
22
#
23
 
24
"""
25
    This file includes some reusable functions and classes that might be useful
26
    to all python scripts
27
"""
28
 
29
import sys
505 farthen 30
from ctypes import *
31
from _ctypes import _SimpleCData
396 farthen 32
 
532 farthen 33
 
34
class Error(Exception):
35
    def __init__(self, value=None):
36
        self.value = value
37
    def __str__(self):
38
        if self.value != None:
39
            return repr(self.value)
40
 
41
class ArgumentError(Error):
42
    pass
43
 
44
class ArgumentTypeError(Error):
45
    def __init__(self, expected, seen=False):
46
        self.expected = expected
47
        self.seen = seen
48
    def __str__(self):
49
        if self.seen:
50
            return "Expected %s but got %s" % (self.expected, self.seen)
51
        else:
52
            return "Expected %s, but saw something else" % self.expected
53
 
54
 
396 farthen 55
class Logger(object):
56
    """
501 farthen 57
        Simple stdout/stderr/file logger.
401 farthen 58
        Loglevel 3 is most verbose, Loglevel 0: Only log something if there is an error.
59
        Loglevel -1 means that nothing is logged.
501 farthen 60
        The write function doesn't care about the loglevel and always logs everything.
396 farthen 61
    """
501 farthen 62
    def __init__(self, loglevel = 2, target = "stderr", logfile = "tools.log"):
400 farthen 63
        """
64
            loglevel: Possible values: 0 (only errors), 1 (warnings), 2 (info,
65
                      recommended for production use), 3 and more (debug)
66
            logfile: File to log to if using the target = "file"
67
            target: Default logging target. Can be "stdout", "file" or "string"
68
        """
69
        self.loglevel = loglevel
70
        self.logfile = logfile
71
        self.target = target
396 farthen 72
 
501 farthen 73
    def write(self, text, indent = 0, target = None):
401 farthen 74
        if self.loglevel >= 0:
75
            if target is None: target = self.target
76
            text = (indent * " ") + text
77
            text = text.replace("\n", "\n" + (indent * " "), text.count("\n") - 1)
78
            if target == "stdout":
610 farthen 79
                if majorver() > 2:
80
                    sys.stdout.write(text)
81
                else:
616 theseven 82
                    sys.stdout.write(text.encode(sys.stdout.encoding or "latin1", "replace"))
501 farthen 83
            if target == "stderr":
610 farthen 84
                if majorver() > 2:
85
                    sys.stdout.write(text)
86
                else:
616 theseven 87
                    sys.stderr.write(text.encode(sys.stderr.encoding or "latin1", "replace"))
401 farthen 88
            elif target == "file":
89
                with open(self.logfile, 'a') as f:
90
                    f.write(text)
91
                    f.close()
92
            elif target == "string":
93
                return text
396 farthen 94
 
400 farthen 95
    def debug(self, text, indent = 0, target = None):
396 farthen 96
        if self.loglevel >= 3:
501 farthen 97
            self.write("DEBUG: " + text, indent, target)
396 farthen 98
 
400 farthen 99
    def info(self, text, indent = 0, target = None):
396 farthen 100
        if self.loglevel >= 2:
501 farthen 101
            self.write(text, indent, target)
396 farthen 102
 
400 farthen 103
    def warn(self, text, indent = 0, target = None):
396 farthen 104
        if self.loglevel >= 1:
501 farthen 105
            self.write("WARNING: " + text, indent, target)
396 farthen 106
 
400 farthen 107
    def error(self, text, indent = 0, target = None):
401 farthen 108
        if self.loglevel >= 0:
501 farthen 109
            self.write("ERROR: " + text, indent, target)
396 farthen 110
 
111
 
112
class Bunch(dict):
113
    """
114
        This is a dict whose items can also be accessed with
115
        bunchinstance.something.
116
    """
117
    def __init__(self, **kw):
118
        dict.__init__(self, kw)
119
        self.__dict__ = self
120
 
121
    def __getstate__(self):
122
        return self
123
 
124
    def __setstate__(self, state):
125
        self.update(state)
126
        self.__dict__ = self
127
 
128
 
607 farthen 129
class remote_pointer(dict):
130
    """
131
        This points to a (remote) location.
132
        Otherwise it behaves like a Bunch.
133
        The second argument must be a Bunch object
134
    """
135
    def __init__(self, address, bunch):
136
        dict.__init__(self, bunch.__dict__)
137
        self.__dict__ = self
138
        self._address_ = address
139
 
140
    def __getstate__(self):
141
        return self
142
 
143
    def __setstate__(self, state):
144
        self.update(state)
145
        self.__dict__ = self
146
 
147
    def __str__(self):
148
        return "<remote_pointer object with address 0x%X>" % (self._address_)
149
 
150
    def __int__(self):
151
        return self._address_
152
 
153
    def __repr__(self):
154
        return self.__str__()
155
 
156
 
505 farthen 157
class c_enum(_SimpleCData):
158
    """
159
        Resembles the enum datatype from C with an 8 bit size.
160
        Returns the associated string of a value with c_enum[i]
161
        Returns the current value of the associated value as c_enum.__repr__()
162
        Comparison operators work with strings and values at the same time.
163
 
164
        ATTENTION: You can not really see if this is initialized or not.
165
        If it is uninitialized it will return the first entry of the enum.
166
        While this may be circumvented by changing the default value to
167
        something else this will not work if the enum is placed inside a
168
        ctypes structure as the __init__() method will not be called then.
169
    """    
170
    _type_ = c_uint8._type_
171
 
172
    def __init__(self, value = 0):
173
        if type(value) == str:
174
            value = getattr(self, value)
175
        _SimpleCData.__init__(self, value)
176
        self[value]
177
 
178
    def __getattr__(self, name):
179
        if name == "value":
180
            return self.value
181
        for key, value in enumerate(self._fields_):
182
            if value == name:
183
                return key
184
 
185
    def __getitem__(self, lookupkey):
186
        for key, value in enumerate(self._fields_):
187
            if key == lookupkey:
188
                return value
189
        raise IndexError("Value %d not in range of possible enum values for %s!" % (lookupkey, self.__class__.__name__))
190
 
191
    def __str__(self):
192
        return self[self.value]
193
 
194
    def __repr__(self):
195
        return self.__str__()
196
 
514 farthen 197
    def __int__(self):
198
        return self.value
199
 
505 farthen 200
    def __eq__(self, other):
201
        if type(other) == str:
202
            try: return getattr(self, other) == self.value
203
            except AttributeError: return False
204
        else:
205
            return self.value == other
206
 
207
    def __lt__(self, other):
208
        if type(other) == str:
209
            try: return self.value < getattr(self, other)
210
            except AttributeError: return False
211
        else:
212
            return self.value < other
213
 
214
    def __gt__(self, other):
215
        if type(other) == str:
216
            try: return  self.value > getattr(self, other)
217
            except AttributeError: return False
218
        else:
219
            return self.value > other
220
 
221
    def __le__(self, other):
222
        if self.value == other or self.value < other:
223
            return True
224
        return False
225
 
226
    def __ge__(self, other):
227
        if self.value == other or self.value > other:
228
            return True
229
        return False
230
 
231
    def __ne__(self, other):
604 farthen 232
        if type(other) == str:
233
            try: return getattr(self, other) != self.value
234
            except AttributeError: return True
235
        else:
236
            return self.value != other
505 farthen 237
 
238
class ExtendedCStruct(LittleEndianStructure):
239
    """
240
        This is a subclass of the LittleEndianStructure.
241
        It implements functions to easily convert
242
        structures to/from strings and Bunches.
243
    """
811 theseven 244
    def __init__(self, data = None, base = 0, address = 0):
245
        LittleEndianStructure.__init__(self)
246
        if data != None:
247
            self._data_ = data
248
            self._base_ = base
249
            self._address_ = address
250
            if address < base or address + sizeof(self) > base + len(data):
251
                raise Exception("Range 0x%08X+0x%X out of bounds [0x%08X:0x%08X]" % (address, sizeof(self), base, base + len(data)))
252
            memmove(addressof(self), data[address - base : address - base + sizeof(self)], sizeof(self))
253
 
254
    def __int__(self):
255
        return self._address_
256
 
505 farthen 257
    def _from_bunch(self, bunch):
258
        for field, _ in self._fields_:
259
            if field in bunch:
260
                setattr(self, field, getattr(bunch, field))
261
 
262
    def _to_bunch(self):
510 farthen 263
        bunch = Bunch()
264
        for field, _ in self._fields_:
265
            setattr(bunch, field, getattr(self, field))
266
        return bunch
505 farthen 267
 
268
    def _from_string(self, string):
269
        memmove(addressof(self), string, sizeof(self))
270
 
271
    def _to_string(self):
272
        return string_at(addressof(self), sizeof(self))
273
 
274
 
607 farthen 275
def getthread(address, threads):
276
    """
277
        Returns the thread at <address> from the list of threads <threads>.
278
        Returns an empty thread if not found
279
    """
280
    for thread in threads:
281
        if address == thread.addr:
282
            return thread
679 farthen 283
    from libemcoredata import scheduler_thread
607 farthen 284
    thread = scheduler_thread()._to_bunch()
680 farthen 285
    thread.name = "[Invalid Thread 0x%08X]" % address
607 farthen 286
    return thread
816 theseven 287
 
288
 
289
def string_from_image(data, base, ptr, maxlen):
290
  if ptr == 0: name = "<NULL>"
291
  try:
292
    string = ""
293
    end = data.find(b"\0", ptr - base, ptr - base + maxlen)
294
    if end < 0: return "<BAD_STRING>"
295
    else:
296
      d = data[ptr - base : end]
297
      for i in range(len(d)):
298
        byte = ord(d[i : i + 1])
299
        if byte < 0x20: return "<BAD_STRING>"
300
        else: string = string + chr(byte)
301
  except: return "<BAD_PTR>"
302
  return string
607 farthen 303
 
304
 
398 farthen 305
def gethwname(id):
306
    try:
532 farthen 307
        from libemcoredata import hwtypes
549 farthen 308
        hwtype = hwtypes[id][1]
398 farthen 309
    except KeyError:
532 farthen 310
        hwtype = "UNKNOWN (ID = 0x%X)" % id
398 farthen 311
    return hwtype
312
 
549 farthen 313
def gethwid(shortname):
314
    from libemcoredata import hwtypes
315
    for hwid in hwtypes:
316
        if hwtypes[hwid][0] == shortname:
317
            return hwid
318
    return False
398 farthen 319
 
549 farthen 320
 
396 farthen 321
def trimdoc(docstring):
322
    """
323
        Trims whitespace from docstrings
324
    """
325
    if not docstring:
326
        return ''
327
    # Convert tabs to spaces (following the normal Python rules)
328
    # and split into a list of lines:
329
    lines = docstring.expandtabs().splitlines()
330
    # Determine minimum indentation (first line doesn't count):
582 farthen 331
    try: maxsize = sys.maxint
332
    except AttributeError: maxsize = sys.maxsize
333
    indent = maxsize
396 farthen 334
    for line in lines[1:]:
335
        stripped = line.lstrip()
336
        if stripped:
337
            indent = min(indent, len(line) - len(stripped))
338
    # Remove indentation (first line is special):
339
    trimmed = [lines[0].strip()]
582 farthen 340
    if indent < maxsize:
396 farthen 341
        for line in lines[1:]:
342
            trimmed.append(line[indent:].rstrip())
343
    # Strip off trailing and leading blank lines:
344
    while trimmed and not trimmed[-1]:
345
        trimmed.pop()
346
    while trimmed and not trimmed[0]:
347
        trimmed.pop(0)
348
    # Return a single string:
349
    return '\n'.join(trimmed)
350
 
351
 
352
def getfuncdoc(funcdict):
353
    """
354
        Extracts important information from a dict of functions like the
355
        docstring and arguments and returns them in a human readable format
356
    """
357
    import inspect
358
    import re
359
    functions = Bunch()
360
    for function in funcdict:
361
        function = funcdict[function].func
362
        docinfo = Bunch()
363
        name = function.__name__
579 farthen 364
        argspec = inspect.getargspec(function)
365
        args = argspec[0]
396 farthen 366
        docinfo['varargs'] = False
579 farthen 367
        if argspec[1]:
396 farthen 368
            docinfo['varargs'] = True
579 farthen 369
        kwargvalues = argspec[3]
370
        kwargs = []
396 farthen 371
        if args:
372
            if kwargvalues:
373
                argnum = len(args) - len(kwargvalues)
374
                kwargnum = len(kwargvalues)
579 farthen 375
                kwargs = list(zip(args[argnum:], kwargvalues))
396 farthen 376
            else:
377
                argnum = len(args)
378
        else:
379
            argnum = 0
380
        docinfo['args'] = args[1:argnum]
381
        docinfo['kwargs'] = kwargs
382
        if function.__doc__:
383
            # strip unneccessary whitespace
384
            docinfo['documentation'] = trimdoc(function.__doc__)
385
        else:
386
            docinfo['documentation'] = None
387
        functions[name] = docinfo
388
    return functions
389
 
390
 
391
def gendoc(funcdict, indentwidth = 4, logtarget = "string"):
392
    logger = Logger()
393
    doc = getfuncdoc(funcdict)
394
    ret = ""
395
    for function in sorted(doc.items()):
396
        function = function[0]
397
        ret += logger.log("def " + function + "(", target = logtarget)
398
        counter = 0
399
        if doc[function]['args']:
400
            for arg in doc[function]['args']:
401
                if counter > 0:
402
                    sys.stdout.write(", ")
403
                counter += 1
404
                ret += logger.log(arg, target = logtarget)
405
        if doc[function]['kwargs']:
406
            for kwarg, kwargvalue in doc[function]['kwargs'].items():
407
                if counter > 0:
408
                    sys.stdout.write(", ")
409
                counter += 1
410
                ret += logger.log(kwarg + "=" + str(kwargvalue), target = logtarget)
411
        if doc[function]['varargs']:
412
            ret += logger.log("*argv", target = logtarget)
413
        ret += logger.log("):\n", target = logtarget)
414
        if doc[function]['documentation']:
415
            ret += logger.log("\"\"\"\n", indent = indentwidth, target = logtarget)
416
            ret += logger.log(trimdoc(doc[function]['documentation']) + "\n", indent = 2 * indentwidth, target = logtarget)
417
            ret += logger.log("\"\"\"\n", indent = indentwidth, target = logtarget)
418
        ret += logger.log("\n", target = logtarget)
532 farthen 419
    return ret
420
 
421
 
422
def to_bool(something):
423
    """
424
        Converts quite everything into bool.
425
    """
584 theseven 426
    if type(something).__name__ in ("bool", "NoneType"):
532 farthen 427
        return something
584 theseven 428
    elif type(something).__name__ in ("int", "long"):
429
        return something != 0
430
    elif type(something) == str:
532 farthen 431
        if something.lower() in ['true', '1', 't', 'y', 'yes']:
432
            return True
433
        elif something.lower() in ['false', '0', 'f', 'n', 'no']:
434
            return False
584 theseven 435
    raise ArgumentTypeError("bool", "'%s' (%s)" % (something, type(something).__name__))
532 farthen 436
 
437
def to_int(something):
438
    """
439
        Converts quite everything to a hexadecimal represented integer.
440
        This works for default arguments too, because it returns
441
        None when it found that it got a NoneType object.
442
    """
584 theseven 443
    if type(something).__name__ in ("int", "long", "NoneType"):
532 farthen 444
        return something
445
    elif type(something) == str:
446
        try:
447
            if something[:2] == "0x": # Hexadecimal notation
448
                return int(something[2:], 16)
449
            elif something[:2] == "0b": # Binary notation
450
                return int(something[2:], 2)
451
            else: # Decimal notation
452
                return int(something, 10)
453
        except ValueError:
584 theseven 454
            raise ArgumentTypeError("integer", "'%s' (%s)" % (something, type(something).__name__))
532 farthen 455
    else:
584 theseven 456
        raise ArgumentTypeError("integer", "'%s' (%s)" % (something, type(something).__name__))
582 farthen 457
 
458
 
459
def majorver():
460
    """
461
        Returns the major version of python
462
    """
463
    import sys
816 theseven 464
    return sys.hexversion // 0x1000000
465