from __future__ import print_function

class MemoryMap(object):
    __slots__ = ("entries",)

    def __init__(self, entries):
        super(MemoryMap, self).__init__()
        self.entries = entries
        return

    def entryContaining(self, address):
        for entry in self.entries:
            if address in entry:
                return entry
        return None

    def __repr__(self):
        return "\n".join([repr(e) for e in self.entries])

    @classmethod
    def forProcess(cls, pid):
        with open("/proc/%d/maps" % pid, "rb") as fd:
            return cls.fromProcFile(fd)

    @classmethod
    def fromProcFile(cls, fd, **kw):
        entries = [MemoryMapEntry.fromProcString(line.rstrip())
                   for line in fd]
        entries.sort(key=lambda x: x.start_address)
        return cls(entries, **kw)
        
class MemoryMapEntry(object):
    __slots__ = ("start_address", "end_address", "readable", "writable",
                 "executable", "shared", "private", "device", "inode",
                 "offset", "pathname")

    def __init__(self, start_address, end_address, readable, writable,
                 executable, shared, private, device, inode, offset,
                 pathname):
        super(MemoryMapEntry, self).__init__()
        self.start_address = start_address
        self.end_address = end_address
        self.readable = readable
        self.writable = writable
        self.executable = executable
        self.shared = shared
        self.private = private
        self.device = device
        self.inode = inode
        self.offset = offset
        self.pathname = pathname
        return

    def __contains__(self, address):
        return entry.start_address <= address < entry.end_address

    def __eq__(self, other):
        return (
            self.__class__ is other.__class__ and
            all([getattr(self, x) == getattr(other, x)
                 for x in self.__slots__]))

    def __ne__(self, other):
        return not self.__eq__(other)

    def __lt__(self, other):
        for attr in self.__slots__:
            my_attr = getattr(self, attr)
            other_attr = getattr(other, attr)
            if my_attr < other_attr:
                return True
            elif my_attr > other_attr:
                return False

        return False

    def __le__(self, other):
        for attr in self.__slots__:
            my_attr = getattr(self, attr)
            other_attr = getattr(other, attr)
            if my_attr < other_attr:
                return True
            elif my_attr > other_attr:
                return False

        return True

    def __gt__(self, other):
        return not self.__le__(other)

    def __ge__(self, other):
        return not self.__lt__(other)

    @property
    def permissions_string(self):
        return (("r" if self.readable else "-") +
                ("w" if self.writable else "-") +
                ("x" if self.executable else "-") +
                ("p" if self.private else ("s" if self.shared else "-")))

    @staticmethod
    def parse_permissions_string(permissions):
        readable = ("r" in permissions)
        writable = ("w" in permissions)
        executable = ("x" in permissions)
        shared = ("s" in permissions)
        private = ("p" in permissions)
        return (readable, writable, executable, shared, private)

    def __repr__(self):
        return "%016x-%016x %s %08x %02x:%02x %8d %s" % (
            self.start_address, self.end_address,
            self.permissions_string,
            self.offset, self.device[0], self.device[1], self.inode,
            (self.pathname if self.pathname is not None else ""))

    @classmethod
    def fromProcString(cls, line, **kw):
        elts = line.split(None, 5)
        if len(elts) < 5:
            raise ValueError("Cannot parse proc/maps line: %r" % line)
        addrRange = elts[0]
        permissions = elts[1]
        offset = elts[2]
        device = elts[3]
        inode = elts[4]
        
        if len(elts) > 5:
            pathname = elts[5]
        else:
            pathname = None

        start_address, end_address = [int(x, 16) for x in addrRange.split("-")]
        offset = int(offset, 16)
        device = [int(x, 16) for x in device.split(":")]
        inode = int(inode)

        readable, writable, executable, shared, private = \
            cls.parse_permissions_string(permissions)
        
        return cls(start_address=start_address, end_address=end_address,
                   readable=readable, writable=writable, executable=executable,
                   shared=shared, private=private, device=device, inode=inode,
                   offset=offset, pathname=pathname, **kw)

if __name__ == "__main__":
    import os
    print(MemoryMap.forProcess(os.getpid()))
##  
##  Local variables:
##  tab-width: 8
##  indent-tabs-mode: nil
##  End:
##  vi: set expandtab tabstop=8
