| #! /usr/bin/env python |
| """bro %s -- compression/decompression utility using the Brotli algorithm.""" |
| |
| from __future__ import print_function |
| import getopt |
| import sys |
| import os |
| import brotli |
| |
| __usage__ = """\ |
| Usage: bro [--force] [--decompress] [--input filename] [--output filename] |
| [--mode 'text'|'font'] [--transform]""" |
| |
| __version__ = '0.1' |
| |
| |
| BROTLI_MODES = { |
| 'text': brotli.MODE_TEXT, |
| 'font': brotli.MODE_FONT |
| } |
| |
| |
| def get_binary_stdio(stream): |
| """ Return the specified standard input, output or errors stream as a |
| 'raw' buffer object suitable for reading/writing binary data from/to it. |
| """ |
| assert stream in ['stdin', 'stdout', 'stderr'], "invalid stream name" |
| stdio = getattr(sys, stream) |
| if sys.version_info[0] < 3: |
| if sys.platform == 'win32': |
| # set I/O stream binary flag on python2.x (Windows) |
| import msvcrt |
| msvcrt.setmode(stdio.fileno(), os.O_BINARY) |
| return stdio |
| else: |
| # get 'buffer' attribute to read/write binary data on python3.x |
| if hasattr(stdio, 'buffer'): |
| return stdio.buffer |
| else: |
| orig_stdio = getattr(sys, "__%s__" % stream) |
| return orig_stdio.buffer |
| |
| |
| def main(args): |
| |
| options = parse_options(args) |
| |
| if options.infile: |
| if not os.path.isfile(options.infile): |
| print('file "%s" not found' % options.infile, file=sys.stderr) |
| sys.exit(1) |
| with open(options.infile, "rb") as infile: |
| data = infile.read() |
| else: |
| if sys.stdin.isatty(): |
| # interactive console, just quit |
| usage() |
| infile = get_binary_stdio('stdin') |
| data = infile.read() |
| |
| if options.outfile: |
| if os.path.isfile(options.outfile) and not options.force: |
| print('output file exists') |
| sys.exit(1) |
| outfile = open(options.outfile, "wb") |
| else: |
| outfile = get_binary_stdio('stdout') |
| |
| try: |
| if options.decompress: |
| data = brotli.decompress(data) |
| else: |
| data = brotli.compress(data, options.mode, options.transform) |
| except brotli.error as e: |
| print('[ERROR] %s: %s' % (e, options.infile or 'sys.stdin'), |
| file=sys.stderr) |
| sys.exit(1) |
| |
| outfile.write(data) |
| outfile.close() |
| |
| |
| def parse_options(args): |
| try: |
| raw_options, dummy = getopt.gnu_getopt( |
| args, "?hdi:o:fm:t", |
| ["help", "decompress", "input=", "output=", "force", "mode=", |
| "transform"]) |
| except getopt.GetoptError as e: |
| print(e, file=sys.stderr) |
| usage() |
| options = Options(raw_options) |
| return options |
| |
| |
| def usage(): |
| print(__usage__, file=sys.stderr) |
| sys.exit(1) |
| |
| |
| class Options(object): |
| |
| def __init__(self, raw_options): |
| self.decompress = self.force = self.transform = False |
| self.infile = self.outfile = None |
| self.mode = BROTLI_MODES['text'] |
| for option, value in raw_options: |
| if option in ("-h", "--help"): |
| print(__doc__ % (__version__)) |
| print("\n%s" % __usage__) |
| sys.exit(0) |
| elif option in ('-d', '--decompress'): |
| self.decompress = True |
| elif option in ('-i', '--input'): |
| self.infile = value |
| elif option in ('-o', '--output'): |
| self.outfile = value |
| elif option in ('-f', '--force'): |
| self.force = True |
| elif option in ('-m', '--mode'): |
| value = value.lower() |
| if value not in ('text', 'font'): |
| print('mode "%s" not recognized' % value, file=sys.stderr) |
| usage() |
| self.mode = BROTLI_MODES[value] |
| elif option in ('-t', '--transform'): |
| self.transform = True |
| |
| |
| if __name__ == '__main__': |
| main(sys.argv[1:]) |