bro.py revision 26fa459c
126fa459cSmrg#! /usr/bin/env python 226fa459cSmrg"""Compression/decompression utility using the Brotli algorithm.""" 326fa459cSmrg 426fa459cSmrgfrom __future__ import print_function 526fa459cSmrgimport argparse 626fa459cSmrgimport sys 726fa459cSmrgimport os 826fa459cSmrgimport platform 926fa459cSmrg 1026fa459cSmrgimport brotli 1126fa459cSmrg 1226fa459cSmrg# default values of encoder parameters 1326fa459cSmrgDEFAULT_PARAMS = { 1426fa459cSmrg 'mode': brotli.MODE_GENERIC, 1526fa459cSmrg 'quality': 11, 1626fa459cSmrg 'lgwin': 22, 1726fa459cSmrg 'lgblock': 0, 1826fa459cSmrg} 1926fa459cSmrg 2026fa459cSmrg 2126fa459cSmrgdef get_binary_stdio(stream): 2226fa459cSmrg """ Return the specified standard input, output or errors stream as a 2326fa459cSmrg 'raw' buffer object suitable for reading/writing binary data from/to it. 2426fa459cSmrg """ 2526fa459cSmrg assert stream in ['stdin', 'stdout', 'stderr'], 'invalid stream name' 2626fa459cSmrg stdio = getattr(sys, stream) 2726fa459cSmrg if sys.version_info[0] < 3: 2826fa459cSmrg if sys.platform == 'win32': 2926fa459cSmrg # set I/O stream binary flag on python2.x (Windows) 3026fa459cSmrg runtime = platform.python_implementation() 3126fa459cSmrg if runtime == 'PyPy': 3226fa459cSmrg # the msvcrt trick doesn't work in pypy, so I use fdopen 3326fa459cSmrg mode = 'rb' if stream == 'stdin' else 'wb' 3426fa459cSmrg stdio = os.fdopen(stdio.fileno(), mode, 0) 3526fa459cSmrg else: 3626fa459cSmrg # this works with CPython -- untested on other implementations 3726fa459cSmrg import msvcrt 3826fa459cSmrg msvcrt.setmode(stdio.fileno(), os.O_BINARY) 3926fa459cSmrg return stdio 4026fa459cSmrg else: 4126fa459cSmrg # get 'buffer' attribute to read/write binary data on python3.x 4226fa459cSmrg if hasattr(stdio, 'buffer'): 4326fa459cSmrg return stdio.buffer 4426fa459cSmrg else: 4526fa459cSmrg orig_stdio = getattr(sys, '__%s__' % stream) 4626fa459cSmrg return orig_stdio.buffer 4726fa459cSmrg 4826fa459cSmrg 4926fa459cSmrgdef main(args=None): 5026fa459cSmrg 5126fa459cSmrg parser = argparse.ArgumentParser( 5226fa459cSmrg prog=os.path.basename(__file__), description=__doc__) 5326fa459cSmrg parser.add_argument( 5426fa459cSmrg '--version', action='version', version=brotli.__version__) 5526fa459cSmrg parser.add_argument( 5626fa459cSmrg '-i', 5726fa459cSmrg '--input', 5826fa459cSmrg metavar='FILE', 5926fa459cSmrg type=str, 6026fa459cSmrg dest='infile', 6126fa459cSmrg help='Input file', 6226fa459cSmrg default=None) 6326fa459cSmrg parser.add_argument( 6426fa459cSmrg '-o', 6526fa459cSmrg '--output', 6626fa459cSmrg metavar='FILE', 6726fa459cSmrg type=str, 6826fa459cSmrg dest='outfile', 6926fa459cSmrg help='Output file', 7026fa459cSmrg default=None) 7126fa459cSmrg parser.add_argument( 7226fa459cSmrg '-f', 7326fa459cSmrg '--force', 7426fa459cSmrg action='store_true', 7526fa459cSmrg help='Overwrite existing output file', 7626fa459cSmrg default=False) 7726fa459cSmrg parser.add_argument( 7826fa459cSmrg '-d', 7926fa459cSmrg '--decompress', 8026fa459cSmrg action='store_true', 8126fa459cSmrg help='Decompress input file', 8226fa459cSmrg default=False) 8326fa459cSmrg params = parser.add_argument_group('optional encoder parameters') 8426fa459cSmrg params.add_argument( 8526fa459cSmrg '-m', 8626fa459cSmrg '--mode', 8726fa459cSmrg metavar='MODE', 8826fa459cSmrg type=int, 8926fa459cSmrg choices=[0, 1, 2], 9026fa459cSmrg help='The compression mode can be 0 for generic input, ' 9126fa459cSmrg '1 for UTF-8 encoded text, or 2 for WOFF 2.0 font data. ' 9226fa459cSmrg 'Defaults to 0.') 9326fa459cSmrg params.add_argument( 9426fa459cSmrg '-q', 9526fa459cSmrg '--quality', 9626fa459cSmrg metavar='QUALITY', 9726fa459cSmrg type=int, 9826fa459cSmrg choices=list(range(0, 12)), 9926fa459cSmrg help='Controls the compression-speed vs compression-density ' 10026fa459cSmrg 'tradeoff. The higher the quality, the slower the ' 10126fa459cSmrg 'compression. Range is 0 to 11. Defaults to 11.') 10226fa459cSmrg params.add_argument( 10326fa459cSmrg '--lgwin', 10426fa459cSmrg metavar='LGWIN', 10526fa459cSmrg type=int, 10626fa459cSmrg choices=list(range(10, 25)), 10726fa459cSmrg help='Base 2 logarithm of the sliding window size. Range is ' 10826fa459cSmrg '10 to 24. Defaults to 22.') 10926fa459cSmrg params.add_argument( 11026fa459cSmrg '--lgblock', 11126fa459cSmrg metavar='LGBLOCK', 11226fa459cSmrg type=int, 11326fa459cSmrg choices=[0] + list(range(16, 25)), 11426fa459cSmrg help='Base 2 logarithm of the maximum input block size. ' 11526fa459cSmrg 'Range is 16 to 24. If set to 0, the value will be set based ' 11626fa459cSmrg 'on the quality. Defaults to 0.') 11726fa459cSmrg # set default values using global DEFAULT_PARAMS dictionary 11826fa459cSmrg parser.set_defaults(**DEFAULT_PARAMS) 11926fa459cSmrg 12026fa459cSmrg options = parser.parse_args(args=args) 12126fa459cSmrg 12226fa459cSmrg if options.infile: 12326fa459cSmrg if not os.path.isfile(options.infile): 12426fa459cSmrg parser.error('file "%s" not found' % options.infile) 12526fa459cSmrg with open(options.infile, 'rb') as infile: 12626fa459cSmrg data = infile.read() 12726fa459cSmrg else: 12826fa459cSmrg if sys.stdin.isatty(): 12926fa459cSmrg # interactive console, just quit 13026fa459cSmrg parser.error('no input') 13126fa459cSmrg infile = get_binary_stdio('stdin') 13226fa459cSmrg data = infile.read() 13326fa459cSmrg 13426fa459cSmrg if options.outfile: 13526fa459cSmrg if os.path.isfile(options.outfile) and not options.force: 13626fa459cSmrg parser.error('output file exists') 13726fa459cSmrg outfile = open(options.outfile, 'wb') 13826fa459cSmrg else: 13926fa459cSmrg outfile = get_binary_stdio('stdout') 14026fa459cSmrg 14126fa459cSmrg try: 14226fa459cSmrg if options.decompress: 14326fa459cSmrg data = brotli.decompress(data) 14426fa459cSmrg else: 14526fa459cSmrg data = brotli.compress( 14626fa459cSmrg data, 14726fa459cSmrg mode=options.mode, 14826fa459cSmrg quality=options.quality, 14926fa459cSmrg lgwin=options.lgwin, 15026fa459cSmrg lgblock=options.lgblock) 15126fa459cSmrg except brotli.error as e: 15226fa459cSmrg parser.exit(1, 15326fa459cSmrg 'bro: error: %s: %s' % (e, options.infile or 'sys.stdin')) 15426fa459cSmrg 15526fa459cSmrg outfile.write(data) 15626fa459cSmrg outfile.close() 15726fa459cSmrg 15826fa459cSmrg 15926fa459cSmrgif __name__ == '__main__': 16026fa459cSmrg main() 161