1#!/usr/bin/env python 2# Copyright (c) 2012 The Chromium Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6 7"""Module that sanitizes source files with specified modifiers.""" 8 9 10from __future__ import print_function 11import commands 12import os 13import sys 14 15 16_FILE_EXTENSIONS_TO_SANITIZE = ['cpp', 'h', 'c'] 17 18_SUBDIRS_TO_IGNORE = ['.git', '.svn', 'third_party'] 19 20 21def SanitizeFilesWithModifiers(directory, file_modifiers, line_modifiers): 22 """Sanitizes source files with the specified file and line modifiers. 23 24 Args: 25 directory: string - The directory which will be recursively traversed to 26 find source files to apply modifiers to. 27 file_modifiers: list - file-modification methods which should be applied to 28 the complete file content (Eg: EOFOneAndOnlyOneNewlineAdder). 29 line_modifiers: list - line-modification methods which should be applied to 30 lines in a file (Eg: TabReplacer). 31 """ 32 for item in os.listdir(directory): 33 34 full_item_path = os.path.join(directory, item) 35 36 if os.path.isfile(full_item_path): # Item is a file. 37 38 # Only sanitize files with extensions we care about. 39 if (len(full_item_path.split('.')) > 1 and 40 full_item_path.split('.')[-1] in _FILE_EXTENSIONS_TO_SANITIZE): 41 f = file(full_item_path) 42 try: 43 lines = f.readlines() 44 finally: 45 f.close() 46 47 new_lines = [] # Collect changed lines here. 48 line_number = 0 # Keeps track of line numbers in the source file. 49 write_to_file = False # File is written to only if this flag is set. 50 51 # Run the line modifiers for each line in this file. 52 for line in lines: 53 original_line = line 54 line_number += 1 55 56 for modifier in line_modifiers: 57 line = modifier(line, full_item_path, line_number) 58 if original_line != line: 59 write_to_file = True 60 new_lines.append(line) 61 62 # Run the file modifiers. 63 old_content = ''.join(lines) 64 new_content = ''.join(new_lines) 65 for modifier in file_modifiers: 66 new_content = modifier(new_content, full_item_path) 67 if new_content != old_content: 68 write_to_file = True 69 70 # Write modifications to the file. 71 if write_to_file: 72 f = file(full_item_path, 'w') 73 try: 74 f.write(new_content) 75 finally: 76 f.close() 77 print('Made changes to %s' % full_item_path) 78 79 elif item not in _SUBDIRS_TO_IGNORE: 80 # Item is a directory recursively call the method. 81 SanitizeFilesWithModifiers(full_item_path, file_modifiers, line_modifiers) 82 83 84############## Line Modification methods ############## 85 86 87def TrailingWhitespaceRemover(line, file_path, line_number): 88 """Strips out trailing whitespaces from the specified line.""" 89 stripped_line = line.rstrip() + '\n' 90 if line != stripped_line: 91 print('Removing trailing whitespace in %s:%s' % (file_path, line_number)) 92 return stripped_line 93 94 95def CrlfReplacer(line, file_path, line_number): 96 """Replaces CRLF with LF.""" 97 if '\r\n' in line: 98 print('Replacing CRLF with LF in %s:%s' % (file_path, line_number)) 99 return line.replace('\r\n', '\n') 100 101 102def TabReplacer(line, file_path, line_number): 103 """Replaces Tabs with 4 whitespaces.""" 104 if '\t' in line: 105 print('Replacing Tab with whitespace in %s:%s' % (file_path, line_number)) 106 return line.replace('\t', ' ') 107 108 109############## File Modification methods ############## 110 111 112def CopywriteChecker(file_content, unused_file_path): 113 """Ensures that the copywrite information is correct.""" 114 # TODO(rmistry): Figure out the legal implications of changing old copyright 115 # headers. 116 return file_content 117 118 119def EOFOneAndOnlyOneNewlineAdder(file_content, file_path): 120 """Adds one and only one LF at the end of the file.""" 121 if file_content and (file_content[-1] != '\n' or file_content[-2:-1] == '\n'): 122 file_content = file_content.rstrip() 123 file_content += '\n' 124 print('Added exactly one newline to %s' % file_path) 125 return file_content 126 127 128def SvnEOLChecker(file_content, file_path): 129 """Sets svn:eol-style property to LF.""" 130 output = commands.getoutput( 131 'svn propget svn:eol-style %s' % file_path) 132 if output != 'LF': 133 print('Setting svn:eol-style property to LF in %s' % file_path) 134 os.system('svn ps svn:eol-style LF %s' % file_path) 135 return file_content 136 137 138####################################################### 139 140 141if '__main__' == __name__: 142 sys.exit(SanitizeFilesWithModifiers( 143 os.getcwd(), 144 file_modifiers=[ 145 CopywriteChecker, 146 EOFOneAndOnlyOneNewlineAdder, 147 SvnEOLChecker, 148 ], 149 line_modifiers=[ 150 CrlfReplacer, 151 TabReplacer, 152 TrailingWhitespaceRemover, 153 ], 154 )) 155