PK[X}M NƊmanly.py""" manly ~~~~~ This script is used (through its' cli) to extract information from manual pages. More specifically, it tells the user, how the given flags modify a command's behaviour. In the code "options" refer to options for manly and "flags" refer to options for the given command. """ from __future__ import print_function __author__ = "Carl Bordum Hansen" __version__ = "0.4.1" import argparse import functools import os import re import subprocess import sys print_err = functools.partial(print, file=sys.stderr) # A backport from subprocess to cover differences between 2/3.4 and 3.5 # This allows the same args to be passed into CPE regardless of version. # This can be replaced with an import at 2.7 EOL # See: https://github.com/carlbordum/manly/issues/27 class CalledProcessError(subprocess.CalledProcessError): def __init__(self, returncode, cmd, output=None, stderr=None): self.returncode = returncode self.cmd = cmd self.output = output self.stderr = stderr _ANSI_BOLD = "%s" if sys.stdout.isatty(): _ANSI_BOLD = "\033[1m%s\033[0m" USAGE_EXAMPLE = """example: $ manly rm --preserve-root -rf rm - remove files or directories ================================ -f, --force ignore nonexistent files and arguments, never prompt --preserve-root do not remove '/' (default) -r, -R, --recursive remove directories and their contents recursively""" VERSION = ( "manly %s\nCopyright (c) 2017 %s.\nMIT License: see LICENSE.\n\n" "Written by %s and Mark Jameson." ) % (__version__, __author__, __author__) def parse_flags(raw_flags): """Return a list of flags. Concatenated flags will be split into individual flags (eg. '-la' -> '-l', '-a'), but the concatenated flag will also be returned, as some command use single dash names (e.g `clang` has flags like "-nostdinc") and some even mix both. """ flags = set() for flag in raw_flags: # Filter out non-flags if not flag.startswith("-"): continue flags.add(flag) # Split and sperately add potential single-letter flags if not flag.startswith("--"): flags.update("-" + char for char in flag[1:]) return list(flags) def parse_manpage(page, flags): """Return a list of blocks that match *flags* in *page*.""" current_section = [] output = [] for line in page.splitlines(): if line: current_section.append(line) continue section = "\n".join(current_section) section_top = section.strip().split("\n")[:2] first_line = section_top[0].split(",") segments = [seg.strip() for seg in first_line] try: segments.append(section_top[1].strip()) except IndexError: pass for flag in flags: for segment in segments: if segment.startswith(flag): output.append( re.sub(r"(^|\s)%s" % flag, _ANSI_BOLD % flag, section).rstrip() ) break current_section = [] return output def manly(command): if isinstance(command, str): command = command.split(" ") program = command[0] flags = command[1:] # we set MANWIDTH, so we don't rely on the users terminal width # try `export MANWIDTH=80` -- makes manuals more readable imo :) man_env = {} man_env.update(os.environ) man_env["MANWIDTH"] = "80" try: process = subprocess.Popen( ["man", "--", program], env=man_env, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) out, err = (s.decode("utf-8") for s in process.communicate()) # emulate subprocess.run of py3.5, for easier changing in the future if process.returncode: raise CalledProcessError( process.returncode, ["man", "--", program], out, err ) except OSError as e: print_err("manly: Could not execute 'man'") print_err(e) sys.exit(127) except CalledProcessError as e: print_err(e.stderr.strip()) sys.exit(e.returncode) manpage = out flags = parse_flags(flags) output = parse_manpage(manpage, flags) title = _ANSI_BOLD % ( re.search(r"(?<=^NAME\n\s{5}).+", manpage, re.MULTILINE).group(0).strip() ) return title, output def main(): parser = argparse.ArgumentParser( prog="manly", description="Explain how FLAGS modify a COMMAND's behaviour.", epilog=USAGE_EXAMPLE, formatter_class=argparse.RawTextHelpFormatter, ) parser.add_argument("command", nargs=argparse.REMAINDER, help="") parser.add_argument( "-v", "--version", action="version", version=VERSION, help="display version information and exit.", ) args = parser.parse_args() if not len(args.command): print_err( "manly: missing COMMAND\nTry 'manly --help' for more information.", ) sys.exit(0) title, output = manly(args.command) if output: print("\n%s" % title) print("=" * (len(title) - 8), end="\n\n") for flag in output: print(flag, end="\n\n") else: print_err("manly: No matching flags found.") if __name__ == "__main__": main() PK!H\"$&manly-0.4.1.dist-info/entry_points.txtN+I/N.,()M˩Vy\\PKѝ/M2R33manly-0.4.1.dist-info/LICENSEMIT License Copyright (c) 2017 Carl Bordum Hansen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK!Hd BUcmanly-0.4.1.dist-info/WHEEL HM K-*ϳR03rOK-J,/RH,rzd&Y)r$[)T&UD"PK!H manly-0.4.1.dist-info/METADATATQo6 ~ׯ`{n!rm %7l(VX,$p}t9@`Hΐd-Ifʚ.ōlV݋Ww;6ͻ[?3' o\(byeۢ^/࿩ M`lKdO0 lv [t 1/Cވ+ WUvi l;JB|vkĥ!Bca50jB(KgqɈOa✷Z&{-} l#8@-SU4튶A)$B⻌SCvz@e o򋟁c8cq4Uh$ěRT ,*]J6!*r!thA2A}>g5[}?j$ūv,G`85G|埶󰶶gejhd̦) -p&~|/ =Xjn`=|8caOClUH S\:*#5d?$ma?B<==pKp9qlck!GU%ՃGHOF#2.27NFx1]g ԝObu[ XYSgV 9?GD> uD7)w+7kGai6NrUkb77VqXHQ{ဳdpk|PMyP"t$xV$&p5t14][Erz;P=0yFMWOqM|>Aiy+ûeCV $|.]+ӽ֯ 'Ma@{H`|ɕ(YNΞZvuzN%+Լ5Rۚ *m ~U W7*kp/l_?2H=K|9wL ]l=,q'QLC]3A._4ltcMPK!HW`%manly-0.4.1.dist-info/RECORDu̻v0нߒPPy bA1EEV !фZWzL>jzn2qx稾GGZǤ}>EU3(x#H X}Ok/{'Q/XsÏfc,%]*ab6'Lϊ]sTBy#{呴8a(N'!<; ~".fb^6$ ̣4'-:Xu4(_&"`k=S5,K;Hrb)b6DWPK[X}M NƊmanly.pyPK!H\"$&manly-0.4.1.dist-info/entry_points.txtPKѝ/M2R33manly-0.4.1.dist-info/LICENSEPK!Hd BUcmanly-0.4.1.dist-info/WHEELPK!H manly-0.4.1.dist-info/METADATAPK!HW`%manly-0.4.1.dist-info/RECORDPK|