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
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
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())
def add_text(self, text): if isinstance(text, str): text = terminalfit(text, width=get_terminal_size()) return super().add_text(text)
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.'))
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