blob: 6b4559704e0f33b2b469f7702c8c6ac191f2f0c4 [file] [log] [blame]
Guido van Rossum105bd981997-07-11 18:39:03 +00001#! /usr/bin/env python
2
Guido van Rossum56f78d91997-05-08 23:11:04 +00003# Conversions to/from quoted-printable transport encoding as per RFC-1521
Guido van Rossumf1945461995-06-14 23:43:44 +00004# (Dec 1991 version).
5
6ESCAPE = '='
7MAXLINESIZE = 76
8HEX = '0123456789ABCDEF'
9
10def needsquoting(c, quotetabs):
11 if c == '\t':
12 return not quotetabs
13 return c == ESCAPE or not(' ' <= c <= '~')
14
15def quote(c):
16 if c == ESCAPE:
17 return ESCAPE * 2
18 else:
19 i = ord(c)
20 return ESCAPE + HEX[i/16] + HEX[i%16]
21
22def encode(input, output, quotetabs):
23 while 1:
24 line = input.readline()
25 if not line: break
26 new = ''
27 last = line[-1:]
28 if last == '\n': line = line[:-1]
29 else: last = ''
30 prev = ''
31 for c in line:
32 if needsquoting(c, quotetabs):
33 c = quote(c)
34 if len(new) + len(c) >= MAXLINESIZE:
35 output.write(new + ESCAPE + '\n')
36 new = ''
37 new = new + c
38 prev = c
39 if prev in (' ', '\t'):
40 output.write(new + ESCAPE + '\n\n')
41 else:
42 output.write(new + '\n')
43
44def decode(input, output):
45 new = ''
46 while 1:
47 line = input.readline()
48 if not line: break
49 i, n = 0, len(line)
50 if n > 0 and line[n-1] == '\n':
51 partial = 0; n = n-1
52 # Strip trailing whitespace
53 while n > 0 and line[n-1] in (' ', '\t'):
54 n = n-1
55 else:
56 partial = 1
57 while i < n:
58 c = line[i]
59 if c <> ESCAPE:
60 new = new + c; i = i+1
61 elif i+1 == n and not partial:
62 partial = 1; break
63 elif i+1 < n and line[i+1] == ESCAPE:
64 new = new + ESCAPE; i = i+2
65 elif i+2 < n and ishex(line[i+1]) and ishex(line[i+2]):
66 new = new + chr(unhex(line[i+1:i+3])); i = i+3
67 else: # Bad escape sequence -- leave it in
68 new = new + c; i = i+1
69 if not partial:
70 output.write(new + '\n')
71 new = ''
72 if new:
73 output.write(new)
74
75def ishex(c):
76 return '0' <= c <= '9' or 'a' <= c <= 'f' or 'A' <= c <= 'F'
77
78def unhex(s):
79 bits = 0
80 for c in s:
81 if '0' <= c <= '9':
82 i = ord('0')
83 elif 'a' <= c <= 'f':
84 i = ord('a')-10
85 elif 'A' <= c <= 'F':
86 i = ord('A')-10
87 else:
88 break
89 bits = bits*16 + (ord(c) - i)
90 return bits
91
92def test():
93 import sys
Guido van Rossum54c15101995-09-18 21:49:24 +000094 import getopt
95 try:
Guido van Rossum8ca84201998-03-26 20:56:10 +000096 opts, args = getopt.getopt(sys.argv[1:], 'td')
Guido van Rossum54c15101995-09-18 21:49:24 +000097 except getopt.error, msg:
Guido van Rossum8ca84201998-03-26 20:56:10 +000098 sys.stdout = sys.stderr
99 print msg
100 print "usage: quopri [-t | -d] [file] ..."
101 print "-t: quote tabs"
102 print "-d: decode; default encode"
103 sys.exit(2)
Guido van Rossum54c15101995-09-18 21:49:24 +0000104 deco = 0
105 tabs = 0
106 for o, a in opts:
Guido van Rossum8ca84201998-03-26 20:56:10 +0000107 if o == '-t': tabs = 1
108 if o == '-d': deco = 1
Guido van Rossum54c15101995-09-18 21:49:24 +0000109 if tabs and deco:
Guido van Rossum8ca84201998-03-26 20:56:10 +0000110 sys.stdout = sys.stderr
111 print "-t and -d are mutually exclusive"
112 sys.exit(2)
Guido van Rossum54c15101995-09-18 21:49:24 +0000113 if not args: args = ['-']
114 sts = 0
115 for file in args:
Guido van Rossum8ca84201998-03-26 20:56:10 +0000116 if file == '-':
117 fp = sys.stdin
118 else:
119 try:
120 fp = open(file)
121 except IOError, msg:
122 sys.stderr.write("%s: can't open (%s)\n" % (file, msg))
123 sts = 1
124 continue
125 if deco:
126 decode(fp, sys.stdout)
127 else:
128 encode(fp, sys.stdout, tabs)
129 if fp is not sys.stdin:
130 fp.close()
Guido van Rossum54c15101995-09-18 21:49:24 +0000131 if sts:
Guido van Rossum8ca84201998-03-26 20:56:10 +0000132 sys.exit(sts)
Guido van Rossumf1945461995-06-14 23:43:44 +0000133
134if __name__ == '__main__':
Guido van Rossum54c15101995-09-18 21:49:24 +0000135 test()