# Conversions to/from quoted-printable transport encoding as per RFC-XXXX
# (Dec 1991 version).

ESCAPE = '='
MAXLINESIZE = 76
HEX = '0123456789ABCDEF'

def needsquoting(c, quotetabs):
	if c == '\t':
		return not quotetabs
	return c == ESCAPE or not(' ' <= c <= '~')

def quote(c):
	if c == ESCAPE:
		return ESCAPE * 2
	else:
		i = ord(c)
		return ESCAPE + HEX[i/16] + HEX[i%16]

def encode(input, output, quotetabs):
	while 1:
		line = input.readline()
		if not line: break
		new = ''
		last = line[-1:]
		if last == '\n': line = line[:-1]
		else: last = ''
		prev = ''
		for c in line:
			if needsquoting(c, quotetabs):
				c = quote(c)
			if len(new) + len(c) >= MAXLINESIZE:
				output.write(new + ESCAPE + '\n')
				new = ''
			new = new + c
			prev = c
		if prev in (' ', '\t'):
			output.write(new + ESCAPE + '\n\n')
		else:
			output.write(new + '\n')

def decode(input, output):
	new = ''
	while 1:
		line = input.readline()
		if not line: break
		i, n = 0, len(line)
		if n > 0 and line[n-1] == '\n':
			partial = 0; n = n-1
			# Strip trailing whitespace
			while n > 0 and line[n-1] in (' ', '\t'):
				n = n-1
		else:
			partial = 1
		while i < n:
			c = line[i]
			if c <> ESCAPE:
				new = new + c; i = i+1
			elif i+1 == n and not partial:
				partial = 1; break
			elif i+1 < n and line[i+1] == ESCAPE:
				new = new + ESCAPE; i = i+2
			elif i+2 < n and ishex(line[i+1]) and ishex(line[i+2]):
				new = new + chr(unhex(line[i+1:i+3])); i = i+3
			else: # Bad escape sequence -- leave it in
				new = new + c; i = i+1
		if not partial:
			output.write(new + '\n')
			new = ''
	if new:
		output.write(new)

def ishex(c):
	return '0' <= c <= '9' or 'a' <= c <= 'f' or 'A' <= c <= 'F'

def unhex(s):
	bits = 0
	for c in s:
		if '0' <= c <= '9':
			i = ord('0')
		elif 'a' <= c <= 'f':
			i = ord('a')-10
		elif 'A' <= c <= 'F':
			i = ord('A')-10
		else:
			break
		bits = bits*16 + (ord(c) - i)
	return bits

def test():
	import sys
	import getopt
	try:
	    opts, args = getopt.getopt(sys.argv[1:], 'td')
	except getopt.error, msg:
	    sys.stdout = sys.stderr
	    print msg
	    print "usage: quopri [-t | -d] [file] ..."
	    print "-t: quote tabs"
	    print "-d: decode; default encode"
	    sys.exit(2)
	deco = 0
	tabs = 0
	for o, a in opts:
	    if o == '-t': tabs = 1
	    if o == '-d': deco = 1
	if tabs and deco:
	    sys.stdout = sys.stderr
	    print "-t and -d are mutually exclusive"
	    sys.exit(2)
	if not args: args = ['-']
	sts = 0
	for file in args:
	    if file == '-':
		fp = sys.stdin
	    else:
		try:
		    fp = open(file)
		except IOError, msg:
		    sys.stderr.write("%s: can't open (%s)\n" % (file, msg))
		    sts = 1
		    continue
	    if deco:
		decode(fp, sys.stdout)
	    else:
		encode(fp, sys.stdout, tabs)
	    if fp is not sys.stdin:
		fp.close()
	if sts:
	    sys.exit(sts)

if __name__ == '__main__':
	test()
