183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#! /usr/bin/env python
283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh"""Conversions to/from quoted-printable transport encoding as per RFC 1521."""
483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# (Dec 1991 version).
683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh__all__ = ["encode", "decode", "encodestring", "decodestring"]
883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehESCAPE = '='
1083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehMAXLINESIZE = 76
1183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehHEX = '0123456789ABCDEF'
1283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehEMPTYSTRING = ''
1383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
1483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehtry:
1583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    from binascii import a2b_qp, b2a_qp
1683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehexcept ImportError:
1783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    a2b_qp = None
1883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    b2a_qp = None
1983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
2083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
2183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef needsquoting(c, quotetabs, header):
2283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Decide whether a particular character needs to be quoted.
2383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
2483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    The 'quotetabs' flag indicates whether embedded tabs and spaces should be
2583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    quoted.  Note that line-ending tabs and spaces are always encoded, as per
2683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    RFC 1521.
2783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
2883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if c in ' \t':
2983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return quotetabs
3083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # if header, we have to escape _ because _ is used to escape space
3183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if c == '_':
3283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return header
3383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return c == ESCAPE or not (' ' <= c <= '~')
3483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
3583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef quote(c):
3683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Quote a single character."""
3783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    i = ord(c)
3883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return ESCAPE + HEX[i//16] + HEX[i%16]
3983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
4083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
4183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
4283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef encode(input, output, quotetabs, header = 0):
4383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Read 'input', apply quoted-printable encoding, and write to 'output'.
4483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
4583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    'input' and 'output' are files with readline() and write() methods.
4683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    The 'quotetabs' flag indicates whether embedded tabs and spaces should be
4783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    quoted.  Note that line-ending tabs and spaces are always encoded, as per
4883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    RFC 1521.
4983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    The 'header' flag indicates whether we are encoding spaces as _ as per
5083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    RFC 1522.
5183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
5283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
5383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if b2a_qp is not None:
5483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        data = input.read()
5583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        odata = b2a_qp(data, quotetabs = quotetabs, header = header)
5683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        output.write(odata)
5783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return
5883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
5983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def write(s, output=output, lineEnd='\n'):
6083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # RFC 1521 requires that the line ending in a space or tab must have
6183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # that trailing character encoded.
6283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if s and s[-1:] in ' \t':
6383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            output.write(s[:-1] + quote(s[-1]) + lineEnd)
6483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        elif s == '.':
6583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            output.write(quote(s) + lineEnd)
6683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
6783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            output.write(s + lineEnd)
6883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
6983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    prevline = None
7083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    while 1:
7183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        line = input.readline()
7283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if not line:
7383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            break
7483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        outline = []
7583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # Strip off any readline induced trailing newline
7683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        stripped = ''
7783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if line[-1:] == '\n':
7883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            line = line[:-1]
7983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            stripped = '\n'
8083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # Calculate the un-length-limited encoded line
8183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        for c in line:
8283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if needsquoting(c, quotetabs, header):
8383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                c = quote(c)
8483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if header and c == ' ':
8583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                outline.append('_')
8683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            else:
8783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                outline.append(c)
8883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # First, write out the previous line
8983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if prevline is not None:
9083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            write(prevline)
9183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # Now see if we need any soft line breaks because of RFC-imposed
9283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # length limitations.  Then do the thisline->prevline dance.
9383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        thisline = EMPTYSTRING.join(outline)
9483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        while len(thisline) > MAXLINESIZE:
9583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # Don't forget to include the soft line break `=' sign in the
9683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # length calculation!
9783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            write(thisline[:MAXLINESIZE-1], lineEnd='=\n')
9883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            thisline = thisline[MAXLINESIZE-1:]
9983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # Write out the current line
10083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        prevline = thisline
10183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Write out the last line, without a trailing newline
10283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if prevline is not None:
10383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        write(prevline, lineEnd=stripped)
10483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
10583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef encodestring(s, quotetabs = 0, header = 0):
10683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if b2a_qp is not None:
10783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return b2a_qp(s, quotetabs = quotetabs, header = header)
10883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    from cStringIO import StringIO
10983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    infp = StringIO(s)
11083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    outfp = StringIO()
11183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    encode(infp, outfp, quotetabs, header)
11283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return outfp.getvalue()
11383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
11483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
11583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
11683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef decode(input, output, header = 0):
11783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Read 'input', apply quoted-printable decoding, and write to 'output'.
11883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    'input' and 'output' are files with readline() and write() methods.
11983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    If 'header' is true, decode underscore as space (per RFC 1522)."""
12083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
12183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if a2b_qp is not None:
12283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        data = input.read()
12383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        odata = a2b_qp(data, header = header)
12483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        output.write(odata)
12583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return
12683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
12783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    new = ''
12883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    while 1:
12983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        line = input.readline()
13083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if not line: break
13183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        i, n = 0, len(line)
13283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if n > 0 and line[n-1] == '\n':
13383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            partial = 0; n = n-1
13483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # Strip trailing whitespace
13583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            while n > 0 and line[n-1] in " \t\r":
13683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                n = n-1
13783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
13883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            partial = 1
13983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        while i < n:
14083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            c = line[i]
14183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if c == '_' and header:
14283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                new = new + ' '; i = i+1
14383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            elif c != ESCAPE:
14483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                new = new + c; i = i+1
14583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            elif i+1 == n and not partial:
14683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                partial = 1; break
14783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            elif i+1 < n and line[i+1] == ESCAPE:
14883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                new = new + ESCAPE; i = i+2
14983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            elif i+2 < n and ishex(line[i+1]) and ishex(line[i+2]):
15083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                new = new + chr(unhex(line[i+1:i+3])); i = i+3
15183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            else: # Bad escape sequence -- leave it in
15283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                new = new + c; i = i+1
15383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if not partial:
15483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            output.write(new + '\n')
15583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            new = ''
15683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if new:
15783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        output.write(new)
15883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
15983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef decodestring(s, header = 0):
16083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if a2b_qp is not None:
16183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return a2b_qp(s, header = header)
16283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    from cStringIO import StringIO
16383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    infp = StringIO(s)
16483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    outfp = StringIO()
16583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    decode(infp, outfp, header = header)
16683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return outfp.getvalue()
16783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
16883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
16983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
17083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Other helper functions
17183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef ishex(c):
17283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Return true if the character 'c' is a hexadecimal digit."""
17383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return '0' <= c <= '9' or 'a' <= c <= 'f' or 'A' <= c <= 'F'
17483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
17583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef unhex(s):
17683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Get the integer value of a hexadecimal number."""
17783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    bits = 0
17883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    for c in s:
17983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if '0' <= c <= '9':
18083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            i = ord('0')
18183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        elif 'a' <= c <= 'f':
18283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            i = ord('a')-10
18383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        elif 'A' <= c <= 'F':
18483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            i = ord('A')-10
18583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
18683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            break
18783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        bits = bits*16 + (ord(c) - i)
18883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return bits
18983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
19083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
19183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
19283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef main():
19383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    import sys
19483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    import getopt
19583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    try:
19683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        opts, args = getopt.getopt(sys.argv[1:], 'td')
19783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    except getopt.error, msg:
19883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        sys.stdout = sys.stderr
19983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        print msg
20083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        print "usage: quopri [-t | -d] [file] ..."
20183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        print "-t: quote tabs"
20283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        print "-d: decode; default encode"
20383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        sys.exit(2)
20483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    deco = 0
20583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    tabs = 0
20683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    for o, a in opts:
20783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if o == '-t': tabs = 1
20883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if o == '-d': deco = 1
20983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if tabs and deco:
21083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        sys.stdout = sys.stderr
21183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        print "-t and -d are mutually exclusive"
21283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        sys.exit(2)
21383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if not args: args = ['-']
21483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    sts = 0
21583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    for file in args:
21683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if file == '-':
21783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            fp = sys.stdin
21883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
21983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            try:
22083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                fp = open(file)
22183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            except IOError, msg:
22283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                sys.stderr.write("%s: can't open (%s)\n" % (file, msg))
22383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                sts = 1
22483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                continue
22583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if deco:
22683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            decode(fp, sys.stdout)
22783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
22883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            encode(fp, sys.stdout, tabs)
22983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if fp is not sys.stdin:
23083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            fp.close()
23183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if sts:
23283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        sys.exit(sts)
23383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
23483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
23583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
23683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehif __name__ == '__main__':
23783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    main()
238