]>
Commit | Line | Data |
---|---|---|
1 | #!/usr/bin/env python | |
2 | # | |
3 | # Copyright 2014 The Rust Project Developers. See the COPYRIGHT | |
4 | # file at the top-level directory of this distribution and at | |
5 | # http://rust-lang.org/COPYRIGHT. | |
6 | # | |
7 | # Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
8 | # http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
9 | # <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
10 | # option. This file may not be copied, modified, or distributed | |
11 | # except according to those terms. | |
12 | ||
13 | import sys | |
14 | import subprocess | |
15 | import re | |
16 | ||
17 | ||
18 | def main(): | |
19 | if len(sys.argv) <= 1: | |
20 | print('Usage: %s [ --apply ] filename1.rs filename2.rs ...' | |
21 | % sys.argv[0]) | |
22 | elif sys.argv[1] == '--apply': | |
23 | for filename in sys.argv[2:]: | |
24 | patch(filename) | |
25 | else: | |
26 | for filename in sys.argv[1:]: | |
27 | diff(filename) | |
28 | ||
29 | ||
30 | def patch(filename): | |
31 | source = read(filename) | |
32 | rewritten = rewrite_bytes_macros(source) | |
33 | if rewritten is not None and rewritten != source: | |
34 | write(filename, rewritten) | |
35 | ||
36 | ||
37 | def diff(filename): | |
38 | rewritten = rewrite_bytes_macros(read(filename)) | |
39 | if rewritten is not None: | |
40 | p = subprocess.Popen(['diff', '-u', filename, '-'], | |
41 | stdin=subprocess.PIPE) | |
42 | p.stdin.write(rewritten) | |
43 | p.stdin.close() | |
44 | p.wait() | |
45 | ||
46 | ||
47 | def read(filename): | |
48 | with open(filename, 'rb') as f: | |
49 | return f.read() | |
50 | ||
51 | ||
52 | def write(filename, content): | |
53 | with open(filename, 'wb') as f: | |
54 | f.write(content) | |
55 | ||
56 | ||
57 | def rewrite_bytes_macros(source): | |
58 | rewritten, num_occurrences = BYTES_MACRO_RE.subn(rewrite_one_macro, source) | |
59 | if num_occurrences > 0: | |
60 | return rewritten | |
61 | ||
62 | ||
63 | BYTES_MACRO_RE = re.compile(br'bytes!\( (?P<args> [^)]* ) \)', re.VERBOSE) | |
64 | ||
65 | ||
66 | def rewrite_one_macro(match): | |
67 | try: | |
68 | bytes = parse_bytes(split_args(match.group('args'))) | |
69 | return b'b"' + b''.join(map(escape, bytes)) + b'"' | |
70 | except SkipThisRewrite: | |
71 | print('Skipped: %s' % match.group(0).decode('utf8', 'replace')) | |
72 | return match.group(0) | |
73 | ||
74 | ||
75 | class SkipThisRewrite(Exception): | |
76 | pass | |
77 | ||
78 | ||
79 | def split_args(args): | |
80 | previous = b'' | |
81 | for arg in args.split(b','): | |
82 | if previous: | |
83 | arg = previous + b',' + arg | |
84 | if arg.count(b'"') % 2 == 0: | |
85 | yield arg | |
86 | previous = b'' | |
87 | else: | |
88 | previous = arg | |
89 | if previous: | |
90 | yield previous | |
91 | ||
92 | ||
93 | def parse_bytes(args): | |
94 | for arg in args: | |
95 | arg = arg.strip() | |
96 | if (arg.startswith(b'"') and arg.endswith(b'"')) or ( | |
97 | arg.startswith(b"'") and arg.endswith(b"'")): | |
98 | # Escaped newline means something different in Rust and Python. | |
99 | if b'\\\n' in arg: | |
100 | raise SkipThisRewrite | |
101 | for byte in eval(b'u' + arg).encode('utf8'): | |
102 | yield ord(byte) | |
103 | else: | |
104 | if arg.endswith(b'u8'): | |
105 | arg = arg[:-2] | |
106 | # Assume that all Rust integer literals | |
107 | # are valid Python integer literals | |
108 | value = int(eval(arg)) | |
109 | assert value <= 0xFF | |
110 | yield value | |
111 | ||
112 | ||
113 | def escape(byte): | |
114 | c = chr(byte) | |
115 | escaped = { | |
116 | b'\0': br'\0', | |
117 | b'\t': br'\t', | |
118 | b'\n': br'\n', | |
119 | b'\r': br'\r', | |
120 | b'\'': b'\\\'', | |
121 | b'\\': br'\\', | |
122 | }.get(c) | |
123 | if escaped is not None: | |
124 | return escaped | |
125 | elif b' ' <= c <= b'~': | |
126 | return chr(byte) | |
127 | else: | |
128 | return ('\\x%02X' % byte).encode('ascii') | |
129 | ||
130 | ||
131 | if str is not bytes: | |
132 | # Python 3.x | |
133 | ord = lambda x: x | |
134 | chr = lambda x: bytes([x]) | |
135 | ||
136 | ||
137 | if __name__ == '__main__': | |
138 | main() |