• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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