uu.py 6.6 KB
Newer Older
1
#! /usr/bin/env python3
2

3 4 5
# Copyright 1994 by Lance Ellinghouse
# Cathedral City, California Republic, United States of America.
#                        All Rights Reserved
6 7
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose and without fee is hereby granted,
8
# provided that the above copyright notice appear in all copies and that
9
# both that copyright notice and this permission notice appear in
10
# supporting documentation, and that the name of Lance Ellinghouse
11
# not be used in advertising or publicity pertaining to distribution
12 13 14 15 16 17 18 19
# of the software without specific, written prior permission.
# LANCE ELLINGHOUSE DISCLAIMS ALL WARRANTIES WITH REGARD TO
# THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
# FITNESS, IN NO EVENT SHALL LANCE ELLINGHOUSE CENTRUM BE LIABLE
# FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 21 22 23 24
#
# Modified by Jack Jansen, CWI, July 1995:
# - Use binascii module to do the actual line-by-line conversion
#   between ascii and binary. This results in a 1000-fold speedup. The C
#   version is still 5 times faster, though.
25
# - Arguments more compliant with python standard
26

27 28 29 30 31
"""Implementation of the UUencode and UUdecode functions.

encode(in_file, out_file [,name, mode])
decode(in_file [, out_file, mode])
"""
32

33
import binascii
34
import os
35
import sys
36

37 38
__all__ = ["Error", "encode", "decode"]

39 40
class Error(Exception):
    pass
41 42 43 44 45 46

def encode(in_file, out_file, name=None, mode=None):
    """Uuencode file"""
    #
    # If in_file is a pathname open it and change defaults
    #
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
    opened_files = []
    try:
        if in_file == '-':
            in_file = sys.stdin.buffer
        elif isinstance(in_file, str):
            if name is None:
                name = os.path.basename(in_file)
            if mode is None:
                try:
                    mode = os.stat(in_file).st_mode
                except AttributeError:
                    pass
            in_file = open(in_file, 'rb')
            opened_files.append(in_file)
        #
        # Open out_file if it is a pathname
        #
        if out_file == '-':
            out_file = sys.stdout.buffer
        elif isinstance(out_file, str):
            out_file = open(out_file, 'wb')
            opened_files.append(out_file)
        #
        # Set defaults for name and mode
        #
72
        if name is None:
73
            name = '-'
74
        if mode is None:
75 76 77 78 79
            mode = 0o666
        #
        # Write the data
        #
        out_file.write(('begin %o %s\n' % ((mode & 0o777), name)).encode("ascii"))
80
        data = in_file.read(45)
81 82 83 84 85 86 87
        while len(data) > 0:
            out_file.write(binascii.b2a_uu(data))
            data = in_file.read(45)
        out_file.write(b' \nend\n')
    finally:
        for f in opened_files:
            f.close()
88 89


90
def decode(in_file, out_file=None, mode=None, quiet=False):
91 92 93 94
    """Decode uuencoded file"""
    #
    # Open the input file, if needed.
    #
95
    opened_files = []
96
    if in_file == '-':
97
        in_file = sys.stdin.buffer
98
    elif isinstance(in_file, str):
99
        in_file = open(in_file, 'rb')
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
        opened_files.append(in_file)

    try:
        #
        # Read until a begin is encountered or we've exhausted the file
        #
        while True:
            hdr = in_file.readline()
            if not hdr:
                raise Error('No valid begin line found in input file')
            if not hdr.startswith(b'begin'):
                continue
            hdrfields = hdr.split(b' ', 2)
            if len(hdrfields) == 3 and hdrfields[0] == b'begin':
                try:
                    int(hdrfields[1], 8)
                    break
                except ValueError:
                    pass
        if out_file is None:
            # If the filename isn't ASCII, what's up with that?!?
            out_file = hdrfields[2].rstrip(b' \t\r\n\f').decode("ascii")
            if os.path.exists(out_file):
                raise Error('Cannot overwrite existing file: %s' % out_file)
        if mode is None:
            mode = int(hdrfields[1], 8)
        #
        # Open the output file
        #
        if out_file == '-':
            out_file = sys.stdout.buffer
        elif isinstance(out_file, str):
            fp = open(out_file, 'wb')
133
            try:
134 135
                os.path.chmod(out_file, mode)
            except AttributeError:
136
                pass
137 138 139 140 141
            out_file = fp
            opened_files.append(out_file)
        #
        # Main decoding loop
        #
142
        s = in_file.readline()
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
        while s and s.strip(b' \t\r\n\f') != b'end':
            try:
                data = binascii.a2b_uu(s)
            except binascii.Error as v:
                # Workaround for broken uuencoders by /Fredrik Lundh
                nbytes = (((s[0]-32) & 63) * 4 + 5) // 3
                data = binascii.a2b_uu(s[:nbytes])
                if not quiet:
                    sys.stderr.write("Warning: %s\n" % v)
            out_file.write(data)
            s = in_file.readline()
        if not s:
            raise Error('Truncated input file')
    finally:
        for f in opened_files:
            f.close()
159 160

def test():
161 162
    """uuencode/uudecode main program"""

163 164 165 166
    import optparse
    parser = optparse.OptionParser(usage='usage: %prog [-d] [-t] [input [output]]')
    parser.add_option('-d', '--decode', dest='decode', help='Decode (instead of encode)?', default=False, action='store_true')
    parser.add_option('-t', '--text', dest='text', help='data is text, encoded format unix-compatible text?', default=False, action='store_true')
167

168 169
    (options, args) = parser.parse_args()
    if len(args) > 2:
170
        parser.error('incorrect number of arguments')
171
        sys.exit(1)
172

173 174 175
    # Use the binary streams underlying stdin/stdout
    input = sys.stdin.buffer
    output = sys.stdout.buffer
176
    if len(args) > 0:
177
        input = args[0]
178
    if len(args) > 1:
179
        output = args[1]
180

181 182
    if options.decode:
        if options.text:
183
            if isinstance(output, str):
184
                output = open(output, 'wb')
185
            else:
186
                print(sys.argv[0], ': cannot do -t to stdout')
187 188
                sys.exit(1)
        decode(input, output)
189
    else:
190
        if options.text:
191
            if isinstance(input, str):
192
                input = open(input, 'rb')
193
            else:
194
                print(sys.argv[0], ': cannot do -t from stdin')
195 196
                sys.exit(1)
        encode(input, output)
197 198 199

if __name__ == '__main__':
    test()