139 lines
3.5 KiB
Python
139 lines
3.5 KiB
Python
|
#!/bin/env python
|
||
|
#
|
||
|
# Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||
|
# file at the top-level directory of this distribution and at
|
||
|
# http://rust-lang.org/COPYRIGHT.
|
||
|
#
|
||
|
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||
|
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||
|
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||
|
# option. This file may not be copied, modified, or distributed
|
||
|
# except according to those terms.
|
||
|
|
||
|
import sys
|
||
|
import subprocess
|
||
|
import re
|
||
|
|
||
|
|
||
|
def main():
|
||
|
if len(sys.argv) <= 1:
|
||
|
print('Usage: %s [ --apply ] filename1.rs filename2.rs ...'
|
||
|
% sys.argv[0])
|
||
|
elif sys.argv[1] == '--apply':
|
||
|
for filename in sys.argv[2:]:
|
||
|
patch(filename)
|
||
|
else:
|
||
|
for filename in sys.argv[1:]:
|
||
|
diff(filename)
|
||
|
|
||
|
|
||
|
def patch(filename):
|
||
|
source = read(filename)
|
||
|
rewritten = rewrite_bytes_macros(source)
|
||
|
if rewritten is not None and rewritten != source:
|
||
|
write(filename, rewritten)
|
||
|
|
||
|
|
||
|
def diff(filename):
|
||
|
rewritten = rewrite_bytes_macros(read(filename))
|
||
|
if rewritten is not None:
|
||
|
p = subprocess.Popen(['diff', '-u', filename, '-'],
|
||
|
stdin=subprocess.PIPE)
|
||
|
p.stdin.write(rewritten)
|
||
|
p.stdin.close()
|
||
|
p.wait()
|
||
|
|
||
|
|
||
|
def read(filename):
|
||
|
with open(filename, 'rb') as f:
|
||
|
return f.read()
|
||
|
|
||
|
|
||
|
def write(filename, content):
|
||
|
with open(filename, 'wb') as f:
|
||
|
f.write(content)
|
||
|
|
||
|
|
||
|
def rewrite_bytes_macros(source):
|
||
|
rewritten, num_occurrences = BYTES_MACRO_RE.subn(rewrite_one_macro, source)
|
||
|
if num_occurrences > 0:
|
||
|
return rewritten
|
||
|
|
||
|
|
||
|
BYTES_MACRO_RE = re.compile(br'bytes!\( (?P<args> [^)]* ) \)', re.VERBOSE)
|
||
|
|
||
|
|
||
|
def rewrite_one_macro(match):
|
||
|
try:
|
||
|
bytes = parse_bytes(split_args(match.group('args')))
|
||
|
return b'b"' + b''.join(map(escape, bytes)) + b'"'
|
||
|
except SkipThisRewrite:
|
||
|
print('Skipped: %s' % match.group(0).decode('utf8', 'replace'))
|
||
|
return match.group(0)
|
||
|
|
||
|
|
||
|
class SkipThisRewrite(Exception):
|
||
|
pass
|
||
|
|
||
|
|
||
|
def split_args(args):
|
||
|
previous = b''
|
||
|
for arg in args.split(b','):
|
||
|
if previous:
|
||
|
arg = previous + b',' + arg
|
||
|
if arg.count(b'"') % 2 == 0:
|
||
|
yield arg
|
||
|
previous = b''
|
||
|
else:
|
||
|
previous = arg
|
||
|
if previous:
|
||
|
yield previous
|
||
|
|
||
|
|
||
|
def parse_bytes(args):
|
||
|
for arg in args:
|
||
|
arg = arg.strip()
|
||
|
if (arg.startswith(b'"') and arg.endswith(b'"')) or (
|
||
|
arg.startswith(b"'") and arg.endswith(b"'")):
|
||
|
# Escaped newline means something different in Rust and Python.
|
||
|
if b'\\\n' in arg:
|
||
|
raise SkipThisRewrite
|
||
|
for byte in eval(b'u' + arg).encode('utf8'):
|
||
|
yield ord(byte)
|
||
|
else:
|
||
|
if arg.endswith(b'u8'):
|
||
|
arg = arg[:-2]
|
||
|
# Assume that all Rust integer literals
|
||
|
# are valid Python integer literals
|
||
|
value = int(eval(arg))
|
||
|
assert value <= 0xFF
|
||
|
yield value
|
||
|
|
||
|
|
||
|
def escape(byte):
|
||
|
c = chr(byte)
|
||
|
escaped = {
|
||
|
b'\0': br'\0',
|
||
|
b'\t': br'\t',
|
||
|
b'\n': br'\n',
|
||
|
b'\r': br'\r',
|
||
|
b'\'': b'\\\'',
|
||
|
b'\\': br'\\',
|
||
|
}.get(c)
|
||
|
if escaped is not None:
|
||
|
return escaped
|
||
|
elif b' ' <= c <= b'~':
|
||
|
return chr(byte)
|
||
|
else:
|
||
|
return ('\\x%02X' % byte).encode('ascii')
|
||
|
|
||
|
|
||
|
if str is not bytes:
|
||
|
# Python 3.x
|
||
|
ord = lambda x: x
|
||
|
chr = lambda x: bytes([x])
|
||
|
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
main()
|