| Guido van Rossum | f194546 | 1995-06-14 23:43:44 +0000 | [diff] [blame] | 1 | # Conversions to/from quoted-printable transport encoding as per RFC-XXXX | 
 | 2 | # (Dec 1991 version). | 
 | 3 |  | 
 | 4 | ESCAPE = '=' | 
 | 5 | MAXLINESIZE = 76 | 
 | 6 | HEX = '0123456789ABCDEF' | 
 | 7 |  | 
 | 8 | def needsquoting(c, quotetabs): | 
 | 9 | 	if c == '\t': | 
 | 10 | 		return not quotetabs | 
 | 11 | 	return c == ESCAPE or not(' ' <= c <= '~') | 
 | 12 |  | 
 | 13 | def quote(c): | 
 | 14 | 	if c == ESCAPE: | 
 | 15 | 		return ESCAPE * 2 | 
 | 16 | 	else: | 
 | 17 | 		i = ord(c) | 
 | 18 | 		return ESCAPE + HEX[i/16] + HEX[i%16] | 
 | 19 |  | 
 | 20 | def encode(input, output, quotetabs): | 
 | 21 | 	while 1: | 
 | 22 | 		line = input.readline() | 
 | 23 | 		if not line: break | 
 | 24 | 		new = '' | 
 | 25 | 		last = line[-1:] | 
 | 26 | 		if last == '\n': line = line[:-1] | 
 | 27 | 		else: last = '' | 
 | 28 | 		prev = '' | 
 | 29 | 		for c in line: | 
 | 30 | 			if needsquoting(c, quotetabs): | 
 | 31 | 				c = quote(c) | 
 | 32 | 			if len(new) + len(c) >= MAXLINESIZE: | 
 | 33 | 				output.write(new + ESCAPE + '\n') | 
 | 34 | 				new = '' | 
 | 35 | 			new = new + c | 
 | 36 | 			prev = c | 
 | 37 | 		if prev in (' ', '\t'): | 
 | 38 | 			output.write(new + ESCAPE + '\n\n') | 
 | 39 | 		else: | 
 | 40 | 			output.write(new + '\n') | 
 | 41 |  | 
 | 42 | def decode(input, output): | 
 | 43 | 	new = '' | 
 | 44 | 	while 1: | 
 | 45 | 		line = input.readline() | 
 | 46 | 		if not line: break | 
 | 47 | 		i, n = 0, len(line) | 
 | 48 | 		if n > 0 and line[n-1] == '\n': | 
 | 49 | 			partial = 0; n = n-1 | 
 | 50 | 			# Strip trailing whitespace | 
 | 51 | 			while n > 0 and line[n-1] in (' ', '\t'): | 
 | 52 | 				n = n-1 | 
 | 53 | 		else: | 
 | 54 | 			partial = 1 | 
 | 55 | 		while i < n: | 
 | 56 | 			c = line[i] | 
 | 57 | 			if c <> ESCAPE: | 
 | 58 | 				new = new + c; i = i+1 | 
 | 59 | 			elif i+1 == n and not partial: | 
 | 60 | 				partial = 1; break | 
 | 61 | 			elif i+1 < n and line[i+1] == ESCAPE: | 
 | 62 | 				new = new + ESCAPE; i = i+2 | 
 | 63 | 			elif i+2 < n and ishex(line[i+1]) and ishex(line[i+2]): | 
 | 64 | 				new = new + chr(unhex(line[i+1:i+3])); i = i+3 | 
 | 65 | 			else: # Bad escape sequence -- leave it in | 
 | 66 | 				new = new + c; i = i+1 | 
 | 67 | 		if not partial: | 
 | 68 | 			output.write(new + '\n') | 
 | 69 | 			new = '' | 
 | 70 | 	if new: | 
 | 71 | 		output.write(new) | 
 | 72 |  | 
 | 73 | def ishex(c): | 
 | 74 | 	return '0' <= c <= '9' or 'a' <= c <= 'f' or 'A' <= c <= 'F' | 
 | 75 |  | 
 | 76 | def unhex(s): | 
 | 77 | 	bits = 0 | 
 | 78 | 	for c in s: | 
 | 79 | 		if '0' <= c <= '9': | 
 | 80 | 			i = ord('0') | 
 | 81 | 		elif 'a' <= c <= 'f': | 
 | 82 | 			i = ord('a')-10 | 
 | 83 | 		elif 'A' <= c <= 'F': | 
 | 84 | 			i = ord('A')-10 | 
 | 85 | 		else: | 
 | 86 | 			break | 
 | 87 | 		bits = bits*16 + (ord(c) - i) | 
 | 88 | 	return bits | 
 | 89 |  | 
 | 90 | def test(): | 
 | 91 | 	import sys | 
| Guido van Rossum | 54c1510 | 1995-09-18 21:49:24 +0000 | [diff] [blame] | 92 | 	import getopt | 
 | 93 | 	try: | 
 | 94 | 	    opts, args = getopt.getopt(sys.argv[1:], 'td') | 
 | 95 | 	except getopt.error, msg: | 
 | 96 | 	    sys.stdout = sys.stderr | 
 | 97 | 	    print msg | 
 | 98 | 	    print "usage: quopri [-t | -d] [file] ..." | 
 | 99 | 	    print "-t: quote tabs" | 
 | 100 | 	    print "-d: decode; default encode" | 
 | 101 | 	    sys.exit(2) | 
 | 102 | 	deco = 0 | 
 | 103 | 	tabs = 0 | 
 | 104 | 	for o, a in opts: | 
 | 105 | 	    if o == '-t': tabs = 1 | 
 | 106 | 	    if o == '-d': deco = 1 | 
 | 107 | 	if tabs and deco: | 
 | 108 | 	    sys.stdout = sys.stderr | 
 | 109 | 	    print "-t and -d are mutually exclusive" | 
 | 110 | 	    sys.exit(2) | 
 | 111 | 	if not args: args = ['-'] | 
 | 112 | 	sts = 0 | 
 | 113 | 	for file in args: | 
 | 114 | 	    if file == '-': | 
 | 115 | 		fp = sys.stdin | 
 | 116 | 	    else: | 
 | 117 | 		try: | 
 | 118 | 		    fp = open(file) | 
 | 119 | 		except IOError, msg: | 
 | 120 | 		    sys.stderr.write("%s: can't open (%s)\n" % (file, msg)) | 
 | 121 | 		    sts = 1 | 
 | 122 | 		    continue | 
 | 123 | 	    if deco: | 
 | 124 | 		decode(fp, sys.stdout) | 
 | 125 | 	    else: | 
 | 126 | 		encode(fp, sys.stdout, tabs) | 
 | 127 | 	    if fp is not sys.stdin: | 
 | 128 | 		fp.close() | 
 | 129 | 	if sts: | 
 | 130 | 	    sys.exit(sts) | 
| Guido van Rossum | f194546 | 1995-06-14 23:43:44 +0000 | [diff] [blame] | 131 |  | 
 | 132 | if __name__ == '__main__': | 
| Guido van Rossum | 54c1510 | 1995-09-18 21:49:24 +0000 | [diff] [blame] | 133 | 	test() |