blob: 004ce4733cf7b7725234cf47b6d56670ef009c02 [file] [log] [blame]
Colin Cross9b1aa0c2020-06-25 17:12:28 -07001# Copyright (C) 2020 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14#
15
16"""This file reads entries from a Ninja rsp file."""
17
18class NinjaRspFileReader:
19 """
20 Reads entries from a Ninja rsp file. Ninja escapes any entries in the file that contain a
21 non-standard character by surrounding the whole entry with single quotes, and then replacing
22 any single quotes in the entry with the escape sequence '\''.
23 """
24
25 def __init__(self, filename):
26 self.f = open(filename, 'r')
27 self.r = self.character_reader(self.f)
28
29 def __iter__(self):
30 return self
31
32 def character_reader(self, f):
33 """Turns a file into a generator that returns one character at a time."""
34 while True:
35 c = f.read(1)
36 if c:
37 yield c
38 else:
39 return
40
41 def __next__(self):
42 entry = self.read_entry()
43 if entry:
44 return entry
45 else:
46 raise StopIteration
47
48 def read_entry(self):
49 c = next(self.r, "")
50 if not c:
51 return ""
52 elif c == "'":
53 return self.read_quoted_entry()
54 else:
55 entry = c
56 for c in self.r:
57 if c == " " or c == "\n":
58 break
59 entry += c
60 return entry
61
62 def read_quoted_entry(self):
63 entry = ""
64 for c in self.r:
65 if c == "'":
66 # Either the end of the quoted entry, or the beginning of an escape sequence, read the next
67 # character to find out.
68 c = next(self.r)
69 if not c or c == " " or c == "\n":
70 # End of the item
71 return entry
72 elif c == "\\":
73 # Escape sequence, expect a '
74 c = next(self.r)
75 if c != "'":
76 # Malformed escape sequence
77 raise "malformed escape sequence %s'\\%s" % (entry, c)
78 entry += "'"
79 else:
80 raise "malformed escape sequence %s'%s" % (entry, c)
81 else:
82 entry += c
83 raise "unterminated quoted entry %s" % entry