|  | # Copyright (C) 2020 The Android Open Source Project | 
|  | # | 
|  | # Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | # you may not use this file except in compliance with the License. | 
|  | # You may obtain a copy of the License at | 
|  | # | 
|  | #      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | # | 
|  | # Unless required by applicable law or agreed to in writing, software | 
|  | # distributed under the License is distributed on an "AS IS" BASIS, | 
|  | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | # See the License for the specific language governing permissions and | 
|  | # limitations under the License. | 
|  | # | 
|  |  | 
|  | """This file reads entries from a Ninja rsp file.""" | 
|  |  | 
|  | class NinjaRspFileReader: | 
|  | """ | 
|  | Reads entries from a Ninja rsp file.  Ninja escapes any entries in the file that contain a | 
|  | non-standard character by surrounding the whole entry with single quotes, and then replacing | 
|  | any single quotes in the entry with the escape sequence '\''. | 
|  | """ | 
|  |  | 
|  | def __init__(self, filename): | 
|  | self.f = open(filename, 'r') | 
|  | self.r = self.character_reader(self.f) | 
|  |  | 
|  | def __iter__(self): | 
|  | return self | 
|  |  | 
|  | def character_reader(self, f): | 
|  | """Turns a file into a generator that returns one character at a time.""" | 
|  | while True: | 
|  | c = f.read(1) | 
|  | if c: | 
|  | yield c | 
|  | else: | 
|  | return | 
|  |  | 
|  | def __next__(self): | 
|  | entry = self.read_entry() | 
|  | if entry: | 
|  | return entry | 
|  | else: | 
|  | raise StopIteration | 
|  |  | 
|  | def read_entry(self): | 
|  | c = next(self.r, "") | 
|  | if not c: | 
|  | return "" | 
|  | elif c == "'": | 
|  | return self.read_quoted_entry() | 
|  | else: | 
|  | entry = c | 
|  | for c in self.r: | 
|  | if c == " " or c == "\n": | 
|  | break | 
|  | entry += c | 
|  | return entry | 
|  |  | 
|  | def read_quoted_entry(self): | 
|  | entry = "" | 
|  | for c in self.r: | 
|  | if c == "'": | 
|  | # Either the end of the quoted entry, or the beginning of an escape sequence, read the next | 
|  | # character to find out. | 
|  | c = next(self.r) | 
|  | if not c or c == " " or c == "\n": | 
|  | # End of the item | 
|  | return entry | 
|  | elif c == "\\": | 
|  | # Escape sequence, expect a ' | 
|  | c = next(self.r) | 
|  | if c != "'": | 
|  | # Malformed escape sequence | 
|  | raise "malformed escape sequence %s'\\%s" % (entry, c) | 
|  | entry += "'" | 
|  | else: | 
|  | raise "malformed escape sequence %s'%s" % (entry, c) | 
|  | else: | 
|  | entry += c | 
|  | raise "unterminated quoted entry %s" % entry |