xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/uu.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
1*cda5da8dSAndroid Build Coastguard Worker#! /usr/bin/env python3
2*cda5da8dSAndroid Build Coastguard Worker
3*cda5da8dSAndroid Build Coastguard Worker# Copyright 1994 by Lance Ellinghouse
4*cda5da8dSAndroid Build Coastguard Worker# Cathedral City, California Republic, United States of America.
5*cda5da8dSAndroid Build Coastguard Worker#                        All Rights Reserved
6*cda5da8dSAndroid Build Coastguard Worker# Permission to use, copy, modify, and distribute this software and its
7*cda5da8dSAndroid Build Coastguard Worker# documentation for any purpose and without fee is hereby granted,
8*cda5da8dSAndroid Build Coastguard Worker# provided that the above copyright notice appear in all copies and that
9*cda5da8dSAndroid Build Coastguard Worker# both that copyright notice and this permission notice appear in
10*cda5da8dSAndroid Build Coastguard Worker# supporting documentation, and that the name of Lance Ellinghouse
11*cda5da8dSAndroid Build Coastguard Worker# not be used in advertising or publicity pertaining to distribution
12*cda5da8dSAndroid Build Coastguard Worker# of the software without specific, written prior permission.
13*cda5da8dSAndroid Build Coastguard Worker# LANCE ELLINGHOUSE DISCLAIMS ALL WARRANTIES WITH REGARD TO
14*cda5da8dSAndroid Build Coastguard Worker# THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
15*cda5da8dSAndroid Build Coastguard Worker# FITNESS, IN NO EVENT SHALL LANCE ELLINGHOUSE CENTRUM BE LIABLE
16*cda5da8dSAndroid Build Coastguard Worker# FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17*cda5da8dSAndroid Build Coastguard Worker# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18*cda5da8dSAndroid Build Coastguard Worker# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19*cda5da8dSAndroid Build Coastguard Worker# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20*cda5da8dSAndroid Build Coastguard Worker#
21*cda5da8dSAndroid Build Coastguard Worker# Modified by Jack Jansen, CWI, July 1995:
22*cda5da8dSAndroid Build Coastguard Worker# - Use binascii module to do the actual line-by-line conversion
23*cda5da8dSAndroid Build Coastguard Worker#   between ascii and binary. This results in a 1000-fold speedup. The C
24*cda5da8dSAndroid Build Coastguard Worker#   version is still 5 times faster, though.
25*cda5da8dSAndroid Build Coastguard Worker# - Arguments more compliant with python standard
26*cda5da8dSAndroid Build Coastguard Worker
27*cda5da8dSAndroid Build Coastguard Worker"""Implementation of the UUencode and UUdecode functions.
28*cda5da8dSAndroid Build Coastguard Worker
29*cda5da8dSAndroid Build Coastguard Workerencode(in_file, out_file [,name, mode], *, backtick=False)
30*cda5da8dSAndroid Build Coastguard Workerdecode(in_file [, out_file, mode, quiet])
31*cda5da8dSAndroid Build Coastguard Worker"""
32*cda5da8dSAndroid Build Coastguard Worker
33*cda5da8dSAndroid Build Coastguard Workerimport binascii
34*cda5da8dSAndroid Build Coastguard Workerimport os
35*cda5da8dSAndroid Build Coastguard Workerimport sys
36*cda5da8dSAndroid Build Coastguard Workerimport warnings
37*cda5da8dSAndroid Build Coastguard Worker
38*cda5da8dSAndroid Build Coastguard Workerwarnings._deprecated(__name__, remove=(3, 13))
39*cda5da8dSAndroid Build Coastguard Worker
40*cda5da8dSAndroid Build Coastguard Worker__all__ = ["Error", "encode", "decode"]
41*cda5da8dSAndroid Build Coastguard Worker
42*cda5da8dSAndroid Build Coastguard Workerclass Error(Exception):
43*cda5da8dSAndroid Build Coastguard Worker    pass
44*cda5da8dSAndroid Build Coastguard Worker
45*cda5da8dSAndroid Build Coastguard Workerdef encode(in_file, out_file, name=None, mode=None, *, backtick=False):
46*cda5da8dSAndroid Build Coastguard Worker    """Uuencode file"""
47*cda5da8dSAndroid Build Coastguard Worker    #
48*cda5da8dSAndroid Build Coastguard Worker    # If in_file is a pathname open it and change defaults
49*cda5da8dSAndroid Build Coastguard Worker    #
50*cda5da8dSAndroid Build Coastguard Worker    opened_files = []
51*cda5da8dSAndroid Build Coastguard Worker    try:
52*cda5da8dSAndroid Build Coastguard Worker        if in_file == '-':
53*cda5da8dSAndroid Build Coastguard Worker            in_file = sys.stdin.buffer
54*cda5da8dSAndroid Build Coastguard Worker        elif isinstance(in_file, str):
55*cda5da8dSAndroid Build Coastguard Worker            if name is None:
56*cda5da8dSAndroid Build Coastguard Worker                name = os.path.basename(in_file)
57*cda5da8dSAndroid Build Coastguard Worker            if mode is None:
58*cda5da8dSAndroid Build Coastguard Worker                try:
59*cda5da8dSAndroid Build Coastguard Worker                    mode = os.stat(in_file).st_mode
60*cda5da8dSAndroid Build Coastguard Worker                except AttributeError:
61*cda5da8dSAndroid Build Coastguard Worker                    pass
62*cda5da8dSAndroid Build Coastguard Worker            in_file = open(in_file, 'rb')
63*cda5da8dSAndroid Build Coastguard Worker            opened_files.append(in_file)
64*cda5da8dSAndroid Build Coastguard Worker        #
65*cda5da8dSAndroid Build Coastguard Worker        # Open out_file if it is a pathname
66*cda5da8dSAndroid Build Coastguard Worker        #
67*cda5da8dSAndroid Build Coastguard Worker        if out_file == '-':
68*cda5da8dSAndroid Build Coastguard Worker            out_file = sys.stdout.buffer
69*cda5da8dSAndroid Build Coastguard Worker        elif isinstance(out_file, str):
70*cda5da8dSAndroid Build Coastguard Worker            out_file = open(out_file, 'wb')
71*cda5da8dSAndroid Build Coastguard Worker            opened_files.append(out_file)
72*cda5da8dSAndroid Build Coastguard Worker        #
73*cda5da8dSAndroid Build Coastguard Worker        # Set defaults for name and mode
74*cda5da8dSAndroid Build Coastguard Worker        #
75*cda5da8dSAndroid Build Coastguard Worker        if name is None:
76*cda5da8dSAndroid Build Coastguard Worker            name = '-'
77*cda5da8dSAndroid Build Coastguard Worker        if mode is None:
78*cda5da8dSAndroid Build Coastguard Worker            mode = 0o666
79*cda5da8dSAndroid Build Coastguard Worker
80*cda5da8dSAndroid Build Coastguard Worker        #
81*cda5da8dSAndroid Build Coastguard Worker        # Remove newline chars from name
82*cda5da8dSAndroid Build Coastguard Worker        #
83*cda5da8dSAndroid Build Coastguard Worker        name = name.replace('\n','\\n')
84*cda5da8dSAndroid Build Coastguard Worker        name = name.replace('\r','\\r')
85*cda5da8dSAndroid Build Coastguard Worker
86*cda5da8dSAndroid Build Coastguard Worker        #
87*cda5da8dSAndroid Build Coastguard Worker        # Write the data
88*cda5da8dSAndroid Build Coastguard Worker        #
89*cda5da8dSAndroid Build Coastguard Worker        out_file.write(('begin %o %s\n' % ((mode & 0o777), name)).encode("ascii"))
90*cda5da8dSAndroid Build Coastguard Worker        data = in_file.read(45)
91*cda5da8dSAndroid Build Coastguard Worker        while len(data) > 0:
92*cda5da8dSAndroid Build Coastguard Worker            out_file.write(binascii.b2a_uu(data, backtick=backtick))
93*cda5da8dSAndroid Build Coastguard Worker            data = in_file.read(45)
94*cda5da8dSAndroid Build Coastguard Worker        if backtick:
95*cda5da8dSAndroid Build Coastguard Worker            out_file.write(b'`\nend\n')
96*cda5da8dSAndroid Build Coastguard Worker        else:
97*cda5da8dSAndroid Build Coastguard Worker            out_file.write(b' \nend\n')
98*cda5da8dSAndroid Build Coastguard Worker    finally:
99*cda5da8dSAndroid Build Coastguard Worker        for f in opened_files:
100*cda5da8dSAndroid Build Coastguard Worker            f.close()
101*cda5da8dSAndroid Build Coastguard Worker
102*cda5da8dSAndroid Build Coastguard Worker
103*cda5da8dSAndroid Build Coastguard Workerdef decode(in_file, out_file=None, mode=None, quiet=False):
104*cda5da8dSAndroid Build Coastguard Worker    """Decode uuencoded file"""
105*cda5da8dSAndroid Build Coastguard Worker    #
106*cda5da8dSAndroid Build Coastguard Worker    # Open the input file, if needed.
107*cda5da8dSAndroid Build Coastguard Worker    #
108*cda5da8dSAndroid Build Coastguard Worker    opened_files = []
109*cda5da8dSAndroid Build Coastguard Worker    if in_file == '-':
110*cda5da8dSAndroid Build Coastguard Worker        in_file = sys.stdin.buffer
111*cda5da8dSAndroid Build Coastguard Worker    elif isinstance(in_file, str):
112*cda5da8dSAndroid Build Coastguard Worker        in_file = open(in_file, 'rb')
113*cda5da8dSAndroid Build Coastguard Worker        opened_files.append(in_file)
114*cda5da8dSAndroid Build Coastguard Worker
115*cda5da8dSAndroid Build Coastguard Worker    try:
116*cda5da8dSAndroid Build Coastguard Worker        #
117*cda5da8dSAndroid Build Coastguard Worker        # Read until a begin is encountered or we've exhausted the file
118*cda5da8dSAndroid Build Coastguard Worker        #
119*cda5da8dSAndroid Build Coastguard Worker        while True:
120*cda5da8dSAndroid Build Coastguard Worker            hdr = in_file.readline()
121*cda5da8dSAndroid Build Coastguard Worker            if not hdr:
122*cda5da8dSAndroid Build Coastguard Worker                raise Error('No valid begin line found in input file')
123*cda5da8dSAndroid Build Coastguard Worker            if not hdr.startswith(b'begin'):
124*cda5da8dSAndroid Build Coastguard Worker                continue
125*cda5da8dSAndroid Build Coastguard Worker            hdrfields = hdr.split(b' ', 2)
126*cda5da8dSAndroid Build Coastguard Worker            if len(hdrfields) == 3 and hdrfields[0] == b'begin':
127*cda5da8dSAndroid Build Coastguard Worker                try:
128*cda5da8dSAndroid Build Coastguard Worker                    int(hdrfields[1], 8)
129*cda5da8dSAndroid Build Coastguard Worker                    break
130*cda5da8dSAndroid Build Coastguard Worker                except ValueError:
131*cda5da8dSAndroid Build Coastguard Worker                    pass
132*cda5da8dSAndroid Build Coastguard Worker        if out_file is None:
133*cda5da8dSAndroid Build Coastguard Worker            # If the filename isn't ASCII, what's up with that?!?
134*cda5da8dSAndroid Build Coastguard Worker            out_file = hdrfields[2].rstrip(b' \t\r\n\f').decode("ascii")
135*cda5da8dSAndroid Build Coastguard Worker            if os.path.exists(out_file):
136*cda5da8dSAndroid Build Coastguard Worker                raise Error(f'Cannot overwrite existing file: {out_file}')
137*cda5da8dSAndroid Build Coastguard Worker            if (out_file.startswith(os.sep) or
138*cda5da8dSAndroid Build Coastguard Worker                f'..{os.sep}' in out_file or (
139*cda5da8dSAndroid Build Coastguard Worker                    os.altsep and
140*cda5da8dSAndroid Build Coastguard Worker                    (out_file.startswith(os.altsep) or
141*cda5da8dSAndroid Build Coastguard Worker                     f'..{os.altsep}' in out_file))
142*cda5da8dSAndroid Build Coastguard Worker               ):
143*cda5da8dSAndroid Build Coastguard Worker                raise Error(f'Refusing to write to {out_file} due to directory traversal')
144*cda5da8dSAndroid Build Coastguard Worker        if mode is None:
145*cda5da8dSAndroid Build Coastguard Worker            mode = int(hdrfields[1], 8)
146*cda5da8dSAndroid Build Coastguard Worker        #
147*cda5da8dSAndroid Build Coastguard Worker        # Open the output file
148*cda5da8dSAndroid Build Coastguard Worker        #
149*cda5da8dSAndroid Build Coastguard Worker        if out_file == '-':
150*cda5da8dSAndroid Build Coastguard Worker            out_file = sys.stdout.buffer
151*cda5da8dSAndroid Build Coastguard Worker        elif isinstance(out_file, str):
152*cda5da8dSAndroid Build Coastguard Worker            fp = open(out_file, 'wb')
153*cda5da8dSAndroid Build Coastguard Worker            os.chmod(out_file, mode)
154*cda5da8dSAndroid Build Coastguard Worker            out_file = fp
155*cda5da8dSAndroid Build Coastguard Worker            opened_files.append(out_file)
156*cda5da8dSAndroid Build Coastguard Worker        #
157*cda5da8dSAndroid Build Coastguard Worker        # Main decoding loop
158*cda5da8dSAndroid Build Coastguard Worker        #
159*cda5da8dSAndroid Build Coastguard Worker        s = in_file.readline()
160*cda5da8dSAndroid Build Coastguard Worker        while s and s.strip(b' \t\r\n\f') != b'end':
161*cda5da8dSAndroid Build Coastguard Worker            try:
162*cda5da8dSAndroid Build Coastguard Worker                data = binascii.a2b_uu(s)
163*cda5da8dSAndroid Build Coastguard Worker            except binascii.Error as v:
164*cda5da8dSAndroid Build Coastguard Worker                # Workaround for broken uuencoders by /Fredrik Lundh
165*cda5da8dSAndroid Build Coastguard Worker                nbytes = (((s[0]-32) & 63) * 4 + 5) // 3
166*cda5da8dSAndroid Build Coastguard Worker                data = binascii.a2b_uu(s[:nbytes])
167*cda5da8dSAndroid Build Coastguard Worker                if not quiet:
168*cda5da8dSAndroid Build Coastguard Worker                    sys.stderr.write("Warning: %s\n" % v)
169*cda5da8dSAndroid Build Coastguard Worker            out_file.write(data)
170*cda5da8dSAndroid Build Coastguard Worker            s = in_file.readline()
171*cda5da8dSAndroid Build Coastguard Worker        if not s:
172*cda5da8dSAndroid Build Coastguard Worker            raise Error('Truncated input file')
173*cda5da8dSAndroid Build Coastguard Worker    finally:
174*cda5da8dSAndroid Build Coastguard Worker        for f in opened_files:
175*cda5da8dSAndroid Build Coastguard Worker            f.close()
176*cda5da8dSAndroid Build Coastguard Worker
177*cda5da8dSAndroid Build Coastguard Workerdef test():
178*cda5da8dSAndroid Build Coastguard Worker    """uuencode/uudecode main program"""
179*cda5da8dSAndroid Build Coastguard Worker
180*cda5da8dSAndroid Build Coastguard Worker    import optparse
181*cda5da8dSAndroid Build Coastguard Worker    parser = optparse.OptionParser(usage='usage: %prog [-d] [-t] [input [output]]')
182*cda5da8dSAndroid Build Coastguard Worker    parser.add_option('-d', '--decode', dest='decode', help='Decode (instead of encode)?', default=False, action='store_true')
183*cda5da8dSAndroid Build Coastguard Worker    parser.add_option('-t', '--text', dest='text', help='data is text, encoded format unix-compatible text?', default=False, action='store_true')
184*cda5da8dSAndroid Build Coastguard Worker
185*cda5da8dSAndroid Build Coastguard Worker    (options, args) = parser.parse_args()
186*cda5da8dSAndroid Build Coastguard Worker    if len(args) > 2:
187*cda5da8dSAndroid Build Coastguard Worker        parser.error('incorrect number of arguments')
188*cda5da8dSAndroid Build Coastguard Worker        sys.exit(1)
189*cda5da8dSAndroid Build Coastguard Worker
190*cda5da8dSAndroid Build Coastguard Worker    # Use the binary streams underlying stdin/stdout
191*cda5da8dSAndroid Build Coastguard Worker    input = sys.stdin.buffer
192*cda5da8dSAndroid Build Coastguard Worker    output = sys.stdout.buffer
193*cda5da8dSAndroid Build Coastguard Worker    if len(args) > 0:
194*cda5da8dSAndroid Build Coastguard Worker        input = args[0]
195*cda5da8dSAndroid Build Coastguard Worker    if len(args) > 1:
196*cda5da8dSAndroid Build Coastguard Worker        output = args[1]
197*cda5da8dSAndroid Build Coastguard Worker
198*cda5da8dSAndroid Build Coastguard Worker    if options.decode:
199*cda5da8dSAndroid Build Coastguard Worker        if options.text:
200*cda5da8dSAndroid Build Coastguard Worker            if isinstance(output, str):
201*cda5da8dSAndroid Build Coastguard Worker                output = open(output, 'wb')
202*cda5da8dSAndroid Build Coastguard Worker            else:
203*cda5da8dSAndroid Build Coastguard Worker                print(sys.argv[0], ': cannot do -t to stdout')
204*cda5da8dSAndroid Build Coastguard Worker                sys.exit(1)
205*cda5da8dSAndroid Build Coastguard Worker        decode(input, output)
206*cda5da8dSAndroid Build Coastguard Worker    else:
207*cda5da8dSAndroid Build Coastguard Worker        if options.text:
208*cda5da8dSAndroid Build Coastguard Worker            if isinstance(input, str):
209*cda5da8dSAndroid Build Coastguard Worker                input = open(input, 'rb')
210*cda5da8dSAndroid Build Coastguard Worker            else:
211*cda5da8dSAndroid Build Coastguard Worker                print(sys.argv[0], ': cannot do -t from stdin')
212*cda5da8dSAndroid Build Coastguard Worker                sys.exit(1)
213*cda5da8dSAndroid Build Coastguard Worker        encode(input, output)
214*cda5da8dSAndroid Build Coastguard Worker
215*cda5da8dSAndroid Build Coastguard Workerif __name__ == '__main__':
216*cda5da8dSAndroid Build Coastguard Worker    test()
217