1"""Python 'uu_codec' Codec - UU content transfer encoding.
2
3This codec de/encodes from bytes to bytes.
4
5Written by Marc-Andre Lemburg (mal@lemburg.com). Some details were
6adapted from uu.py which was written by Lance Ellinghouse and
7modified by Jack Jansen and Fredrik Lundh.
8"""
9
10import codecs
11import binascii
12from io import BytesIO
13
14### Codec APIs
15
16def uu_encode(input, errors='strict', filename='<data>', mode=0o666):
17    assert errors == 'strict'
18    infile = BytesIO(input)
19    outfile = BytesIO()
20    read = infile.read
21    write = outfile.write
22
23    # Encode
24    write(('begin %o %s\n' % (mode & 0o777, filename)).encode('ascii'))
25    chunk = read(45)
26    while chunk:
27        write(binascii.b2a_uu(chunk))
28        chunk = read(45)
29    write(b' \nend\n')
30
31    return (outfile.getvalue(), len(input))
32
33def uu_decode(input, errors='strict'):
34    assert errors == 'strict'
35    infile = BytesIO(input)
36    outfile = BytesIO()
37    readline = infile.readline
38    write = outfile.write
39
40    # Find start of encoded data
41    while 1:
42        s = readline()
43        if not s:
44            raise ValueError('Missing "begin" line in input data')
45        if s[:5] == b'begin':
46            break
47
48    # Decode
49    while True:
50        s = readline()
51        if not s or s == b'end\n':
52            break
53        try:
54            data = binascii.a2b_uu(s)
55        except binascii.Error as v:
56            # Workaround for broken uuencoders by /Fredrik Lundh
57            nbytes = (((s[0]-32) & 63) * 4 + 5) // 3
58            data = binascii.a2b_uu(s[:nbytes])
59            #sys.stderr.write("Warning: %s\n" % str(v))
60        write(data)
61    if not s:
62        raise ValueError('Truncated input data')
63
64    return (outfile.getvalue(), len(input))
65
66class Codec(codecs.Codec):
67    def encode(self, input, errors='strict'):
68        return uu_encode(input, errors)
69
70    def decode(self, input, errors='strict'):
71        return uu_decode(input, errors)
72
73class IncrementalEncoder(codecs.IncrementalEncoder):
74    def encode(self, input, final=False):
75        return uu_encode(input, self.errors)[0]
76
77class IncrementalDecoder(codecs.IncrementalDecoder):
78    def decode(self, input, final=False):
79        return uu_decode(input, self.errors)[0]
80
81class StreamWriter(Codec, codecs.StreamWriter):
82    charbuffertype = bytes
83
84class StreamReader(Codec, codecs.StreamReader):
85    charbuffertype = bytes
86
87### encodings module API
88
89def getregentry():
90    return codecs.CodecInfo(
91        name='uu',
92        encode=uu_encode,
93        decode=uu_decode,
94        incrementalencoder=IncrementalEncoder,
95        incrementaldecoder=IncrementalDecoder,
96        streamreader=StreamReader,
97        streamwriter=StreamWriter,
98        _is_text_encoding=False,
99    )
100