blob: 5bb11f653d7fed76f886eb4079e794d412ad6da5 [file] [log] [blame]
Guido van Rossum105bd981997-07-11 18:39:03 +00001#! /usr/bin/env python
2
Guido van Rossume7b146f2000-02-04 15:28:42 +00003"""Conversions to/from quoted-printable transport encoding as per RFC-1521."""
4
Guido van Rossumf1945461995-06-14 23:43:44 +00005# (Dec 1991 version).
6
7ESCAPE = '='
8MAXLINESIZE = 76
9HEX = '0123456789ABCDEF'
10
11def needsquoting(c, quotetabs):
Jeremy Hylton77249442000-10-05 17:24:33 +000012 """Decide whether a particular character needs to be quoted.
Guido van Rossume7b146f2000-02-04 15:28:42 +000013
Jeremy Hylton77249442000-10-05 17:24:33 +000014 The 'quotetabs' flag indicates whether tabs should be quoted."""
15 if c == '\t':
16 return not quotetabs
17 return c == ESCAPE or not(' ' <= c <= '~')
Guido van Rossumf1945461995-06-14 23:43:44 +000018
19def quote(c):
Jeremy Hylton77249442000-10-05 17:24:33 +000020 """Quote a single character."""
21 i = ord(c)
22 return ESCAPE + HEX[i/16] + HEX[i%16]
Guido van Rossumf1945461995-06-14 23:43:44 +000023
24def encode(input, output, quotetabs):
Jeremy Hylton77249442000-10-05 17:24:33 +000025 """Read 'input', apply quoted-printable encoding, and write to 'output'.
Guido van Rossume7b146f2000-02-04 15:28:42 +000026
Jeremy Hylton77249442000-10-05 17:24:33 +000027 'input' and 'output' are files with readline() and write() methods.
28 The 'quotetabs' flag indicates whether tabs should be quoted.
Tim Peters2344fae2001-01-15 00:50:52 +000029 """
Jeremy Hylton77249442000-10-05 17:24:33 +000030 while 1:
31 line = input.readline()
32 if not line:
33 break
34 new = ''
35 last = line[-1:]
36 if last == '\n':
37 line = line[:-1]
38 else:
39 last = ''
40 prev = ''
41 for c in line:
42 if needsquoting(c, quotetabs):
43 c = quote(c)
44 if len(new) + len(c) >= MAXLINESIZE:
45 output.write(new + ESCAPE + '\n')
46 new = ''
47 new = new + c
48 prev = c
49 if prev in (' ', '\t'):
50 output.write(new + ESCAPE + '\n\n')
51 else:
52 output.write(new + '\n')
Guido van Rossumf1945461995-06-14 23:43:44 +000053
54def decode(input, output):
Jeremy Hylton77249442000-10-05 17:24:33 +000055 """Read 'input', apply quoted-printable decoding, and write to 'output'.
Guido van Rossume7b146f2000-02-04 15:28:42 +000056
Jeremy Hylton77249442000-10-05 17:24:33 +000057 'input' and 'output' are files with readline() and write() methods."""
58 new = ''
59 while 1:
60 line = input.readline()
61 if not line: break
62 i, n = 0, len(line)
63 if n > 0 and line[n-1] == '\n':
64 partial = 0; n = n-1
65 # Strip trailing whitespace
66 while n > 0 and line[n-1] in (' ', '\t'):
67 n = n-1
68 else:
69 partial = 1
70 while i < n:
71 c = line[i]
Fred Drake8152d322000-12-12 23:20:45 +000072 if c != ESCAPE:
Jeremy Hylton77249442000-10-05 17:24:33 +000073 new = new + c; i = i+1
74 elif i+1 == n and not partial:
75 partial = 1; break
76 elif i+1 < n and line[i+1] == ESCAPE:
77 new = new + ESCAPE; i = i+2
78 elif i+2 < n and ishex(line[i+1]) and ishex(line[i+2]):
79 new = new + chr(unhex(line[i+1:i+3])); i = i+3
80 else: # Bad escape sequence -- leave it in
81 new = new + c; i = i+1
82 if not partial:
83 output.write(new + '\n')
84 new = ''
85 if new:
86 output.write(new)
Guido van Rossumf1945461995-06-14 23:43:44 +000087
88def ishex(c):
Jeremy Hylton77249442000-10-05 17:24:33 +000089 """Return true if the character 'c' is a hexadecimal digit."""
90 return '0' <= c <= '9' or 'a' <= c <= 'f' or 'A' <= c <= 'F'
Guido van Rossumf1945461995-06-14 23:43:44 +000091
92def unhex(s):
Jeremy Hylton77249442000-10-05 17:24:33 +000093 """Get the integer value of a hexadecimal number."""
94 bits = 0
95 for c in s:
96 if '0' <= c <= '9':
97 i = ord('0')
98 elif 'a' <= c <= 'f':
99 i = ord('a')-10
100 elif 'A' <= c <= 'F':
101 i = ord('A')-10
102 else:
103 break
104 bits = bits*16 + (ord(c) - i)
105 return bits
Guido van Rossumf1945461995-06-14 23:43:44 +0000106
107def test():
Jeremy Hylton77249442000-10-05 17:24:33 +0000108 import sys
109 import getopt
110 try:
111 opts, args = getopt.getopt(sys.argv[1:], 'td')
112 except getopt.error, msg:
113 sys.stdout = sys.stderr
114 print msg
115 print "usage: quopri [-t | -d] [file] ..."
116 print "-t: quote tabs"
117 print "-d: decode; default encode"
118 sys.exit(2)
119 deco = 0
120 tabs = 0
121 for o, a in opts:
122 if o == '-t': tabs = 1
123 if o == '-d': deco = 1
124 if tabs and deco:
125 sys.stdout = sys.stderr
126 print "-t and -d are mutually exclusive"
127 sys.exit(2)
128 if not args: args = ['-']
129 sts = 0
130 for file in args:
131 if file == '-':
132 fp = sys.stdin
133 else:
134 try:
135 fp = open(file)
136 except IOError, msg:
137 sys.stderr.write("%s: can't open (%s)\n" % (file, msg))
138 sts = 1
139 continue
140 if deco:
141 decode(fp, sys.stdout)
142 else:
143 encode(fp, sys.stdout, tabs)
144 if fp is not sys.stdin:
145 fp.close()
146 if sts:
147 sys.exit(sts)
Guido van Rossumf1945461995-06-14 23:43:44 +0000148
149if __name__ == '__main__':
Jeremy Hylton77249442000-10-05 17:24:33 +0000150 test()