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