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