#!/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 or the MIT license # , 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 [^)]* ) \)', 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()