3 """Conversions to/from quoted-printable transport encoding as per RFC 1521."""
7 __all__
= ["encode", "decode", "encodestring", "decodestring"]
11 HEX
= '0123456789ABCDEF'
16 def needsquoting(c
, quotetabs
):
17 """Decide whether a particular character needs to be quoted.
19 The 'quotetabs' flag indicates whether embedded tabs and spaces should be
20 quoted. Note that line-ending tabs and spaces are always encoded, as per
25 return c
== ESCAPE
or not (' ' <= c
<= '~')
28 """Quote a single character."""
30 return ESCAPE
+ HEX
[i
/16] + HEX
[i
%16]
34 def encode(input, output
, quotetabs
):
35 """Read 'input', apply quoted-printable encoding, and write to 'output'.
37 'input' and 'output' are files with readline() and write() methods.
38 The 'quotetabs' flag indicates whether embedded tabs and spaces should be
39 quoted. Note that line-ending tabs and spaces are always encoded, as per
42 def write(s
, output
=output
, lineEnd
='\n'):
43 # RFC 1521 requires that the line ending in a space or tab must have
44 # that trailing character encoded.
45 if s
and s
[-1:] in ' \t':
46 output
.write(s
[:-1] + quote(s
[-1]) + lineEnd
)
48 output
.write(s
+ lineEnd
)
52 line
= input.readline()
56 # Strip off any readline induced trailing newline
61 # Calculate the un-length-limited encoded line
63 if needsquoting(c
, quotetabs
):
66 # First, write out the previous line
67 if prevline
is not None:
69 # Now see if we need any soft line breaks because of RFC-imposed
70 # length limitations. Then do the thisline->prevline dance.
71 thisline
= EMPTYSTRING
.join(outline
)
72 while len(thisline
) > MAXLINESIZE
:
73 # Don't forget to include the soft line break `=' sign in the
75 write(thisline
[:MAXLINESIZE
-1], lineEnd
='=\n')
76 thisline
= thisline
[MAXLINESIZE
-1:]
77 # Write out the current line
79 # Write out the last line, without a trailing newline
80 if prevline
is not None:
81 write(prevline
, lineEnd
=stripped
)
83 def encodestring(s
, quotetabs
=0):
84 from cStringIO
import StringIO
87 encode(infp
, outfp
, quotetabs
)
88 return outfp
.getvalue()
92 def decode(input, output
):
93 """Read 'input', apply quoted-printable decoding, and write to 'output'.
95 'input' and 'output' are files with readline() and write() methods."""
98 line
= input.readline()
101 if n
> 0 and line
[n
-1] == '\n':
103 # Strip trailing whitespace
104 while n
> 0 and line
[n
-1] in " \t\r":
111 new
= new
+ c
; i
= i
+1
112 elif i
+1 == n
and not partial
:
114 elif i
+1 < n
and line
[i
+1] == ESCAPE
:
115 new
= new
+ ESCAPE
; i
= i
+2
116 elif i
+2 < n
and ishex(line
[i
+1]) and ishex(line
[i
+2]):
117 new
= new
+ chr(unhex(line
[i
+1:i
+3])); i
= i
+3
118 else: # Bad escape sequence -- leave it in
119 new
= new
+ c
; i
= i
+1
121 output
.write(new
+ '\n')
127 from cStringIO
import StringIO
131 return outfp
.getvalue()
135 # Other helper functions
137 """Return true if the character 'c' is a hexadecimal digit."""
138 return '0' <= c
<= '9' or 'a' <= c
<= 'f' or 'A' <= c
<= 'F'
141 """Get the integer value of a hexadecimal number."""
146 elif 'a' <= c
<= 'f':
148 elif 'A' <= c
<= 'F':
152 bits
= bits
*16 + (ord(c
) - i
)
161 opts
, args
= getopt
.getopt(sys
.argv
[1:], 'td')
162 except getopt
.error
, msg
:
163 sys
.stdout
= sys
.stderr
165 print "usage: quopri [-t | -d] [file] ..."
166 print "-t: quote tabs"
167 print "-d: decode; default encode"
172 if o
== '-t': tabs
= 1
173 if o
== '-d': deco
= 1
175 sys
.stdout
= sys
.stderr
176 print "-t and -d are mutually exclusive"
178 if not args
: args
= ['-']
187 sys
.stderr
.write("%s: can't open (%s)\n" % (file, msg
))
191 decode(fp
, sys
.stdout
)
193 encode(fp
, sys
.stdout
, tabs
)
194 if fp
is not sys
.stdin
:
201 if __name__
== '__main__':