blob: 7a094b4c166cca145a4473f3f9c14f42cf9bf436 [file] [log] [blame]
Cosimo Lupob33e10e2015-03-16 17:56:10 +00001#! /usr/bin/env python
Alex Nicksay4651f7c2016-12-12 04:28:15 -05002"""Compression/decompression utility using the Brotli algorithm."""
Cosimo Lupob33e10e2015-03-16 17:56:10 +00003
4from __future__ import print_function
Cosimo Lupo32c44ec2015-05-08 14:17:20 +01005import argparse
Cosimo Lupob33e10e2015-03-16 17:56:10 +00006import sys
7import os
Cosimo Lupob316cb72015-04-16 12:41:40 +01008import platform
Cosimo Lupob33e10e2015-03-16 17:56:10 +00009
Alex Nicksay4651f7c2016-12-12 04:28:15 -050010import brotli
Cosimo Lupob33e10e2015-03-16 17:56:10 +000011
Cosimo Lupo4106a402015-05-08 15:46:56 +010012# default values of encoder parameters
13DEFAULT_PARAMS = {
Cosimo Lupoc93c0da2015-05-11 11:10:48 +010014 'mode': brotli.MODE_GENERIC,
Cosimo Lupo4106a402015-05-08 15:46:56 +010015 'quality': 11,
16 'lgwin': 22,
17 'lgblock': 0,
Cosimo Lupob33e10e2015-03-16 17:56:10 +000018}
19
20
21def get_binary_stdio(stream):
22 """ Return the specified standard input, output or errors stream as a
23 'raw' buffer object suitable for reading/writing binary data from/to it.
24 """
Alex Nicksay4651f7c2016-12-12 04:28:15 -050025 assert stream in ['stdin', 'stdout', 'stderr'], 'invalid stream name'
Cosimo Lupob33e10e2015-03-16 17:56:10 +000026 stdio = getattr(sys, stream)
27 if sys.version_info[0] < 3:
28 if sys.platform == 'win32':
29 # set I/O stream binary flag on python2.x (Windows)
Cosimo Lupob316cb72015-04-16 12:41:40 +010030 runtime = platform.python_implementation()
Alex Nicksay4651f7c2016-12-12 04:28:15 -050031 if runtime == 'PyPy':
Cosimo Lupob316cb72015-04-16 12:41:40 +010032 # the msvcrt trick doesn't work in pypy, so I use fdopen
Alex Nicksay4651f7c2016-12-12 04:28:15 -050033 mode = 'rb' if stream == 'stdin' else 'wb'
Cosimo Lupob316cb72015-04-16 12:41:40 +010034 stdio = os.fdopen(stdio.fileno(), mode, 0)
35 else:
36 # this works with CPython -- untested on other implementations
37 import msvcrt
38 msvcrt.setmode(stdio.fileno(), os.O_BINARY)
Cosimo Lupob33e10e2015-03-16 17:56:10 +000039 return stdio
40 else:
41 # get 'buffer' attribute to read/write binary data on python3.x
42 if hasattr(stdio, 'buffer'):
43 return stdio.buffer
44 else:
Alex Nicksay4651f7c2016-12-12 04:28:15 -050045 orig_stdio = getattr(sys, '__%s__' % stream)
Cosimo Lupob33e10e2015-03-16 17:56:10 +000046 return orig_stdio.buffer
47
48
Cosimo Lupo82ffc4d2015-10-05 18:57:32 +010049def main(args=None):
Cosimo Lupob33e10e2015-03-16 17:56:10 +000050
Cosimo Lupo32c44ec2015-05-08 14:17:20 +010051 parser = argparse.ArgumentParser(
Alex Nicksay4651f7c2016-12-12 04:28:15 -050052 prog=os.path.basename(__file__), description=__doc__)
53 parser.add_argument(
54 '--version', action='version', version=brotli.__version__)
55 parser.add_argument(
56 '-i',
57 '--input',
58 metavar='FILE',
59 type=str,
60 dest='infile',
61 help='Input file',
62 default=None)
63 parser.add_argument(
64 '-o',
65 '--output',
66 metavar='FILE',
67 type=str,
68 dest='outfile',
69 help='Output file',
70 default=None)
71 parser.add_argument(
72 '-f',
73 '--force',
74 action='store_true',
75 help='Overwrite existing output file',
76 default=False)
77 parser.add_argument(
78 '-d',
79 '--decompress',
80 action='store_true',
81 help='Decompress input file',
82 default=False)
Cosimo Lupo4106a402015-05-08 15:46:56 +010083 params = parser.add_argument_group('optional encoder parameters')
Alex Nicksay4651f7c2016-12-12 04:28:15 -050084 params.add_argument(
85 '-m',
86 '--mode',
87 metavar='MODE',
88 type=int,
89 choices=[0, 1, 2],
90 help='The compression mode can be 0 for generic input, '
91 '1 for UTF-8 encoded text, or 2 for WOFF 2.0 font data. '
92 'Defaults to 0.')
93 params.add_argument(
94 '-q',
95 '--quality',
96 metavar='QUALITY',
97 type=int,
98 choices=list(range(0, 12)),
99 help='Controls the compression-speed vs compression-density '
100 'tradeoff. The higher the quality, the slower the '
101 'compression. Range is 0 to 11. Defaults to 11.')
102 params.add_argument(
103 '--lgwin',
104 metavar='LGWIN',
105 type=int,
106 choices=list(range(10, 25)),
107 help='Base 2 logarithm of the sliding window size. Range is '
108 '10 to 24. Defaults to 22.')
109 params.add_argument(
110 '--lgblock',
111 metavar='LGBLOCK',
112 type=int,
113 choices=[0] + list(range(16, 25)),
114 help='Base 2 logarithm of the maximum input block size. '
115 'Range is 16 to 24. If set to 0, the value will be set based '
116 'on the quality. Defaults to 0.')
Cosimo Lupo4106a402015-05-08 15:46:56 +0100117 # set default values using global DEFAULT_PARAMS dictionary
118 parser.set_defaults(**DEFAULT_PARAMS)
Cosimo Lupo32c44ec2015-05-08 14:17:20 +0100119
Cosimo Lupo82ffc4d2015-10-05 18:57:32 +0100120 options = parser.parse_args(args=args)
Cosimo Lupob33e10e2015-03-16 17:56:10 +0000121
122 if options.infile:
123 if not os.path.isfile(options.infile):
Cosimo Lupo32c44ec2015-05-08 14:17:20 +0100124 parser.error('file "%s" not found' % options.infile)
Alex Nicksay4651f7c2016-12-12 04:28:15 -0500125 with open(options.infile, 'rb') as infile:
Cosimo Lupob33e10e2015-03-16 17:56:10 +0000126 data = infile.read()
127 else:
128 if sys.stdin.isatty():
129 # interactive console, just quit
Cosimo Lupo32c44ec2015-05-08 14:17:20 +0100130 parser.error('no input')
Cosimo Lupob33e10e2015-03-16 17:56:10 +0000131 infile = get_binary_stdio('stdin')
132 data = infile.read()
133
134 if options.outfile:
135 if os.path.isfile(options.outfile) and not options.force:
Cosimo Lupo32c44ec2015-05-08 14:17:20 +0100136 parser.error('output file exists')
Alex Nicksay4651f7c2016-12-12 04:28:15 -0500137 outfile = open(options.outfile, 'wb')
Cosimo Lupob33e10e2015-03-16 17:56:10 +0000138 else:
139 outfile = get_binary_stdio('stdout')
140
141 try:
142 if options.decompress:
Eugene Kliuchnikovd63e8f72017-08-04 10:02:56 +0200143 data = brotli.decompress(data)
Cosimo Lupob33e10e2015-03-16 17:56:10 +0000144 else:
Cosimo Lupo4106a402015-05-08 15:46:56 +0100145 data = brotli.compress(
Alex Nicksay4651f7c2016-12-12 04:28:15 -0500146 data,
147 mode=options.mode,
148 quality=options.quality,
149 lgwin=options.lgwin,
Eugene Kliuchnikovd63e8f72017-08-04 10:02:56 +0200150 lgblock=options.lgblock)
Cosimo Lupob33e10e2015-03-16 17:56:10 +0000151 except brotli.error as e:
Alex Nicksay4651f7c2016-12-12 04:28:15 -0500152 parser.exit(1,
153 'bro: error: %s: %s' % (e, options.infile or 'sys.stdin'))
Cosimo Lupob33e10e2015-03-16 17:56:10 +0000154
155 outfile.write(data)
156 outfile.close()
157
158
Cosimo Lupob33e10e2015-03-16 17:56:10 +0000159if __name__ == '__main__':
Cosimo Lupo32c44ec2015-05-08 14:17:20 +0100160 main()