Issue #7117 (backport py3k float repr) continued:
[python.git] / Lib / quopri.py
blob8788afc2f188faeb357442e130431dfa569ff571
1 #! /usr/bin/env python
3 """Conversions to/from quoted-printable transport encoding as per RFC 1521."""
5 # (Dec 1991 version).
7 __all__ = ["encode", "decode", "encodestring", "decodestring"]
9 ESCAPE = '='
10 MAXLINESIZE = 76
11 HEX = '0123456789ABCDEF'
12 EMPTYSTRING = ''
14 try:
15 from binascii import a2b_qp, b2a_qp
16 except ImportError:
17 a2b_qp = None
18 b2a_qp = None
21 def needsquoting(c, quotetabs, header):
22 """Decide whether a particular character needs to be quoted.
24 The 'quotetabs' flag indicates whether embedded tabs and spaces should be
25 quoted. Note that line-ending tabs and spaces are always encoded, as per
26 RFC 1521.
27 """
28 if c in ' \t':
29 return quotetabs
30 # if header, we have to escape _ because _ is used to escape space
31 if c == '_':
32 return header
33 return c == ESCAPE or not (' ' <= c <= '~')
35 def quote(c):
36 """Quote a single character."""
37 i = ord(c)
38 return ESCAPE + HEX[i//16] + HEX[i%16]
42 def encode(input, output, quotetabs, header = 0):
43 """Read 'input', apply quoted-printable encoding, and write to 'output'.
45 'input' and 'output' are files with readline() and write() methods.
46 The 'quotetabs' flag indicates whether embedded tabs and spaces should be
47 quoted. Note that line-ending tabs and spaces are always encoded, as per
48 RFC 1521.
49 The 'header' flag indicates whether we are encoding spaces as _ as per
50 RFC 1522.
51 """
53 if b2a_qp is not None:
54 data = input.read()
55 odata = b2a_qp(data, quotetabs = quotetabs, header = header)
56 output.write(odata)
57 return
59 def write(s, output=output, lineEnd='\n'):
60 # RFC 1521 requires that the line ending in a space or tab must have
61 # that trailing character encoded.
62 if s and s[-1:] in ' \t':
63 output.write(s[:-1] + quote(s[-1]) + lineEnd)
64 elif s == '.':
65 output.write(quote(s) + lineEnd)
66 else:
67 output.write(s + lineEnd)
69 prevline = None
70 while 1:
71 line = input.readline()
72 if not line:
73 break
74 outline = []
75 # Strip off any readline induced trailing newline
76 stripped = ''
77 if line[-1:] == '\n':
78 line = line[:-1]
79 stripped = '\n'
80 # Calculate the un-length-limited encoded line
81 for c in line:
82 if needsquoting(c, quotetabs, header):
83 c = quote(c)
84 if header and c == ' ':
85 outline.append('_')
86 else:
87 outline.append(c)
88 # First, write out the previous line
89 if prevline is not None:
90 write(prevline)
91 # Now see if we need any soft line breaks because of RFC-imposed
92 # length limitations. Then do the thisline->prevline dance.
93 thisline = EMPTYSTRING.join(outline)
94 while len(thisline) > MAXLINESIZE:
95 # Don't forget to include the soft line break `=' sign in the
96 # length calculation!
97 write(thisline[:MAXLINESIZE-1], lineEnd='=\n')
98 thisline = thisline[MAXLINESIZE-1:]
99 # Write out the current line
100 prevline = thisline
101 # Write out the last line, without a trailing newline
102 if prevline is not None:
103 write(prevline, lineEnd=stripped)
105 def encodestring(s, quotetabs = 0, header = 0):
106 if b2a_qp is not None:
107 return b2a_qp(s, quotetabs = quotetabs, header = header)
108 from cStringIO import StringIO
109 infp = StringIO(s)
110 outfp = StringIO()
111 encode(infp, outfp, quotetabs, header)
112 return outfp.getvalue()
116 def decode(input, output, header = 0):
117 """Read 'input', apply quoted-printable decoding, and write to 'output'.
118 'input' and 'output' are files with readline() and write() methods.
119 If 'header' is true, decode underscore as space (per RFC 1522)."""
121 if a2b_qp is not None:
122 data = input.read()
123 odata = a2b_qp(data, header = header)
124 output.write(odata)
125 return
127 new = ''
128 while 1:
129 line = input.readline()
130 if not line: break
131 i, n = 0, len(line)
132 if n > 0 and line[n-1] == '\n':
133 partial = 0; n = n-1
134 # Strip trailing whitespace
135 while n > 0 and line[n-1] in " \t\r":
136 n = n-1
137 else:
138 partial = 1
139 while i < n:
140 c = line[i]
141 if c == '_' and header:
142 new = new + ' '; i = i+1
143 elif c != ESCAPE:
144 new = new + c; i = i+1
145 elif i+1 == n and not partial:
146 partial = 1; break
147 elif i+1 < n and line[i+1] == ESCAPE:
148 new = new + ESCAPE; i = i+2
149 elif i+2 < n and ishex(line[i+1]) and ishex(line[i+2]):
150 new = new + chr(unhex(line[i+1:i+3])); i = i+3
151 else: # Bad escape sequence -- leave it in
152 new = new + c; i = i+1
153 if not partial:
154 output.write(new + '\n')
155 new = ''
156 if new:
157 output.write(new)
159 def decodestring(s, header = 0):
160 if a2b_qp is not None:
161 return a2b_qp(s, header = header)
162 from cStringIO import StringIO
163 infp = StringIO(s)
164 outfp = StringIO()
165 decode(infp, outfp, header = header)
166 return outfp.getvalue()
170 # Other helper functions
171 def ishex(c):
172 """Return true if the character 'c' is a hexadecimal digit."""
173 return '0' <= c <= '9' or 'a' <= c <= 'f' or 'A' <= c <= 'F'
175 def unhex(s):
176 """Get the integer value of a hexadecimal number."""
177 bits = 0
178 for c in s:
179 if '0' <= c <= '9':
180 i = ord('0')
181 elif 'a' <= c <= 'f':
182 i = ord('a')-10
183 elif 'A' <= c <= 'F':
184 i = ord('A')-10
185 else:
186 break
187 bits = bits*16 + (ord(c) - i)
188 return bits
192 def main():
193 import sys
194 import getopt
195 try:
196 opts, args = getopt.getopt(sys.argv[1:], 'td')
197 except getopt.error, msg:
198 sys.stdout = sys.stderr
199 print msg
200 print "usage: quopri [-t | -d] [file] ..."
201 print "-t: quote tabs"
202 print "-d: decode; default encode"
203 sys.exit(2)
204 deco = 0
205 tabs = 0
206 for o, a in opts:
207 if o == '-t': tabs = 1
208 if o == '-d': deco = 1
209 if tabs and deco:
210 sys.stdout = sys.stderr
211 print "-t and -d are mutually exclusive"
212 sys.exit(2)
213 if not args: args = ['-']
214 sts = 0
215 for file in args:
216 if file == '-':
217 fp = sys.stdin
218 else:
219 try:
220 fp = open(file)
221 except IOError, msg:
222 sys.stderr.write("%s: can't open (%s)\n" % (file, msg))
223 sts = 1
224 continue
225 if deco:
226 decode(fp, sys.stdout)
227 else:
228 encode(fp, sys.stdout, tabs)
229 if fp is not sys.stdin:
230 fp.close()
231 if sts:
232 sys.exit(sts)
236 if __name__ == '__main__':
237 main()