~ubuntu-branches/ubuntu/karmic/pypy/karmic

« back to all changes in this revision

Viewing changes to lib-python/2.4.1/quopri.py

  • Committer: Bazaar Package Importer
  • Author(s): Alexandre Fayolle
  • Date: 2007-04-13 09:33:09 UTC
  • Revision ID: james.westby@ubuntu.com-20070413093309-yoojh4jcoocu2krz
Tags: upstream-1.0.0
ImportĀ upstreamĀ versionĀ 1.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#! /usr/bin/env python
 
2
 
 
3
"""Conversions to/from quoted-printable transport encoding as per RFC 1521."""
 
4
 
 
5
# (Dec 1991 version).
 
6
 
 
7
__all__ = ["encode", "decode", "encodestring", "decodestring"]
 
8
 
 
9
ESCAPE = '='
 
10
MAXLINESIZE = 76
 
11
HEX = '0123456789ABCDEF'
 
12
EMPTYSTRING = ''
 
13
 
 
14
try:
 
15
    from binascii import a2b_qp, b2a_qp
 
16
except ImportError:
 
17
    a2b_qp = None
 
18
    b2a_qp = None
 
19
 
 
20
 
 
21
def needsquoting(c, quotetabs, header):
 
22
    """Decide whether a particular character needs to be quoted.
 
23
 
 
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 <= '~')
 
34
 
 
35
def quote(c):
 
36
    """Quote a single character."""
 
37
    i = ord(c)
 
38
    return ESCAPE + HEX[i//16] + HEX[i%16]
 
39
 
 
40
 
 
41
 
 
42
def encode(input, output, quotetabs, header = 0):
 
43
    """Read 'input', apply quoted-printable encoding, and write to 'output'.
 
44
 
 
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
    """
 
52
 
 
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
 
58
 
 
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)
 
68
 
 
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)
 
104
 
 
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()
 
113
 
 
114
 
 
115
 
 
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)."""
 
120
 
 
121
    if a2b_qp is not None:
 
122
        data = input.read()
 
123
        odata = a2b_qp(data, header = header)
 
124
        output.write(odata)
 
125
        return
 
126
 
 
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)
 
158
 
 
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()
 
167
 
 
168
 
 
169
 
 
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'
 
174
 
 
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
 
189
 
 
190
 
 
191
 
 
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)
 
233
 
 
234
 
 
235
 
 
236
if __name__ == '__main__':
 
237
    main()