Beispiel #1
0
    def process(self, data):
        colorize = not self.args.gray and not self.args.stdout
        if os.name == 'nt' and not self.isatty:
            # coloring stderr does not work properly in Windows when stdout is redirected:
            # https://github.com/tartley/colorama/issues/200
            colorize = False
        lines = self._peeklines(data, colorize)

        if self.args.stdout:
            for line in lines:
                yield line.encode(self.codec)
            return

        stderr = sys.stderr

        if colorize:
            colorama = self._colorama
            if os.name == 'nt':
                stderr = colorama.AnsiToWin32(stderr).stream
            _erase = ' ' * get_terminal_size()
            _reset = F'\r{colorama.Style.RESET_ALL}{_erase}\r'
        else:
            _reset = ''

        try:
            for line in lines:
                print(line, file=stderr)
        except BaseException:
            stderr.write(_reset)
            raise
        if not self.isatty:
            self.log_info('forwarding input to next unit')
            yield data
Beispiel #2
0
 def get_max_width(self):
     width = self.max_width
     if not width:
         width = get_terminal_size()
         width = width and width or 75
         self.max_width = width
     return width
Beispiel #3
0
 def __init__(self,
              prog,
              indent_increment=2,
              max_help_position=30,
              width=None):
     super().__init__(prog,
                      indent_increment,
                      max_help_position,
                      width=get_terminal_size())
Beispiel #4
0
 def add_text(self, text):
     if isinstance(text, str):
         text = terminalfit(text, width=get_terminal_size())
     return super().add_text(text)
Beispiel #5
0
def explorer(keyword_color='91', unit_color='93'):
    """
    Main routine of the Binary Refinery Explorer.
    """

    try:
        import colorama
        colorama.init()
    except ModuleNotFoundError:
        pass

    headline = (
        R'_________ __       __________                   __                      '
        '\n'
        R'\_____   \__| ____/   ______/___ __  _________ |  |   ___________ ____  '
        '\n'
        R' ||  | __/  |/    \  /__|  / __ \  \/  /\____ \|  |  /  _ \_  __ \ __ \ '
        '\n'
        R' ||  |   \  |   |  \_  _| \  ___/>    < |  |_] |  |_(  |_| |  | \/ ___/ '
        '\n'
        R' ||____  /__|___|__/  / |  \____]__/\__\|   __/|____/\____/|__|  \____] '
        '\n'
        R' ======\/=========\  /==|__|============|__|=========================== '
        '\n'
        R'                   \/  binary refinery unit keyword search              '
        '\n')

    headline = highlight_word(headline, 'keyword', keyword_color)
    headline = highlight_word(headline, 'unit', unit_color)
    print(headline)

    argp = argparse.ArgumentParser()
    argp.add_argument(
        'keywords',
        metavar='keyword',
        type=lambda s: s.lower(),
        nargs='+',
        help=
        'Provide keywords; if any of these keywords match the help description of '
        'a unit, it will be listed.')
    argp.add_argument(
        '-T',
        dest='terminal_width',
        metavar='COLS',
        type=int,
        default=80,
        help='Set the terminal width, the default is 80 characters. Setting '
        'the width to zero (-T0) will use the full terminal width.')
    argp.add_argument('-I',
                      '--conjunctive',
                      dest='quantifier',
                      action='store_const',
                      const=all,
                      default=any,
                      help='All keywords must match rather than any of them.')
    argp.add_argument(
        '-b',
        '--brief',
        action='store_true',
        help=
        'Only search and display unit descriptions, not the full help output')
    argp.add_argument('-c',
                      '--case-sensitive',
                      action='store_true',
                      help='Make the search case sensitive.')
    argp.add_argument('-v',
                      '--verbose',
                      action='store_true',
                      help='Be verbose.')
    argp.add_argument(
        '-w',
        '--words',
        action='store_true',
        help='Keywords only match if they appear as words, not as substrings.')
    argp.add_argument('-x',
                      '--no-wildcards',
                      dest='wildcards',
                      action='store_false',
                      help='Do not allow wildcards in search string')
    args = argp.parse_args()
    width = args.terminal_width or get_terminal_size()
    separator = '-' * width
    result = False

    def pattern(keyword):
        kw = re.escape(keyword)
        if args.wildcards:
            kw = kw.replace(R'\*', R'\S*')
            kw = kw.replace(R'\?', R'\S')
        if args.words:
            kw = RF'(?<!\w)(?:{kw})(?!\w)'
        if not args.case_sensitive:
            kw = '(?i)' + kw
        if args.verbose:
            print(F'-- final regex: {kw}')
        return re.compile(kw)

    args.keywords = [pattern(k) for k in args.keywords]

    for name in refinery.__all__:
        unit = getattr(refinery, name)

        try:
            if not issubclass(
                    unit,
                    refinery.units.Entry) or unit is refinery.units.Entry:
                continue
        except TypeError:
            continue

        info = get_help_string(unit, args.brief, width)

        if not args.quantifier(
                k.search(name) or k.search(info) for k in args.keywords):
            continue

        result = True

        for kw in args.keywords:
            info = highlight(info, kw, keyword_color)

        if args.brief:
            header = '{e:-<4}[{}]{e:-<{w}}'.format(name,
                                                   w=width - len(name) - 6,
                                                   e='')
            header = highlight_word(header, name, unit_color)
        else:
            info = highlight_word(info, name, unit_color)
            header = separator

        print(header, info, sep='\n')

    print(separator if result else ('No matching unit was found.'))
Beispiel #6
0
    def process(self, data):
        colorama = self._colorama
        colorama.init(autoreset=False, convert=True)
        from sys import stderr

        nobg = not self.args.background
        meta = metavars(data)

        label = meta.format_str(self.args.label, self.codec, [data])
        if label and not label.endswith(' '):
            label += ' '

        bgmap = [
            colorama.Back.BLACK,
            colorama.Back.WHITE,
            colorama.Back.YELLOW,
            colorama.Back.CYAN,
            colorama.Back.BLUE,
            colorama.Back.GREEN,
            colorama.Back.LIGHTRED_EX,
            colorama.Back.MAGENTA,
        ]
        fgmap = [
            colorama.Fore.LIGHTBLACK_EX,
            colorama.Fore.LIGHTWHITE_EX,
            colorama.Fore.LIGHTYELLOW_EX,
            colorama.Fore.LIGHTCYAN_EX,
            colorama.Fore.LIGHTBLUE_EX,
            colorama.Fore.LIGHTGREEN_EX,
            colorama.Fore.LIGHTRED_EX,
            colorama.Fore.LIGHTMAGENTA_EX,
        ]

        _reset = colorama.Back.BLACK + colorama.Fore.WHITE + colorama.Style.RESET_ALL

        clrmap = fgmap if nobg else bgmap
        footer = '{}] [{:>7}]\n'.format(_reset, repr(meta['entropy']))
        header = '[{1}{0}] ['.format(
            _reset, ''.join(F'{bg}{k}' for k, bg in enumerate(clrmap, 1)))
        header_length = 4 + len(clrmap)
        footer_length = 4 + len(str(meta['entropy']))

        width = get_terminal_size() - header_length - footer_length - 1
        if width < 16:
            raise RuntimeError(
                F'computed terminal width {width} is too small for heatmap')

        def entropy_select(value, map):
            index = min(len(map) - 1, math.floor(value * len(map)))
            return map[index]

        view = memoryview(data)
        size = len(data)
        chunk_size = 0

        for block_size in range(1, width + 1):
            block_count = width // block_size
            chunk_size = size // block_count
            if chunk_size > 1024:
                break

        q, remainder = divmod(width, block_size)
        assert q == block_count
        indices = list(range(q))
        random.seed(sum(view[:1024]))
        random.shuffle(indices)

        block_sizes = [block_size] * q
        q, r = divmod(remainder, block_count)
        for i in indices:
            block_sizes[i] += q
        for i in indices[:r]:
            block_sizes[i] += 1
        assert sum(block_sizes) == width

        q, remainder = divmod(size, block_count)
        assert q == chunk_size
        chunk_sizes = [chunk_size] * block_count
        for i in indices[:remainder]:
            chunk_sizes[i] += 1
        assert sum(chunk_sizes) == size

        stream = MemoryFile(view)
        filler = self.args.block_char if nobg else ' '

        try:
            stderr.write(header)
            if label is not None:
                stderr.write(colorama.Fore.WHITE)
                stderr.flush()
            it = itertools.chain(label, itertools.cycle(filler))
            for chunk_size, block_size in zip(chunk_sizes, block_sizes):
                chunk = stream.read(chunk_size)
                chunk_entropy = entropy(chunk)
                string = entropy_select(chunk_entropy, clrmap) + ''.join(
                    itertools.islice(it, block_size))
                stderr.write(string)
                stderr.flush()
        except BaseException:
            eraser = ' ' * width
            stderr.write(F'\r{_reset}{eraser}\r')
            raise
        else:
            stderr.write(footer)
            stderr.flush()
        if not self.isatty:
            yield data