1# Copyright 2018 - 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"""File-related utilities.""" 16 17 18import os 19import shutil 20import tempfile 21 22 23def make_parent_dirs(file_path): 24 """Creates parent directories for the file_path.""" 25 if os.path.exists(file_path): 26 return 27 28 parent_dir = os.path.dirname(file_path) 29 if parent_dir and not os.path.exists(parent_dir): 30 os.makedirs(parent_dir) 31 32 33def remove_redundant_line_markers(lines): 34 """ 35 Removes any redundant line markers. 36 37 Line markers are to support better error reporting for neverallow rules. 38 Line markers, possibly nested, look like: 39 40 ;;* lm(s|x) LINENO FILENAME 41 (CIL STATEMENTS) 42 ;;* lme 43 44 * lms is used when each of the following CIL statements corresponds to a line 45 in the original file. 46 47 * lmx is used when the following CIL statements are all expanded from a 48 single high-level language line. 49 50 * lme ends a line mark block. 51 52 Redundant line markers are markers without any statements inside. Normally 53 there are no such redundant line markers, but CIL files filtered out by 54 filter_out function below may contain those. remove_redundant_line_markers 55 find all such redundant line markers and removes all of them. See 56 file_utils_test.py for an example. 57 """ 58 59 marker_stk = [] 60 valid = [False] * len(lines) 61 62 for idx in range(len(lines)): 63 line = lines[idx] 64 if line.startswith(";;* lmx") or line.startswith(";;* lms"): 65 # line start marker 66 marker_stk.append(idx) 67 elif line.startswith(";;* lme"): # line end marker 68 if valid[marker_stk[-1]]: 69 valid[idx] = True 70 # propagate valid to parent markers 71 if len(marker_stk) >= 2: 72 valid[marker_stk[-2]] = True 73 marker_stk.pop() 74 else: 75 # any other expressions 76 valid[idx] = True 77 # set the current marker as valid 78 if marker_stk: 79 valid[marker_stk[-1]] = True 80 81 return [lines[idx] for idx in range(len(lines)) if valid[idx]] 82 83def filter_out(pattern_files, input_file): 84 """"Removes lines in input_file that match any line in pattern_files.""" 85 86 # Prepares patterns. 87 patterns = [] 88 for f in pattern_files: 89 patterns.extend([x for x in open(f).readlines() if not x.startswith(";;*")]) 90 91 # Copy lines that are not in the pattern. 92 tmp_output = tempfile.NamedTemporaryFile(mode='w+') 93 with open(input_file, 'r') as in_file: 94 lines = [line for line in in_file.readlines() 95 if line not in patterns and line.strip()] 96 lines = remove_redundant_line_markers(lines) 97 tmp_output.writelines(lines) 98 99 # Append empty line because a completely empty file 100 # will trip up secilc later on: 101 tmp_output.write("\n") 102 tmp_output.flush() 103 104 # Replaces the input_file. 105 shutil.copyfile(tmp_output.name, input_file) 106