|  | #!/usr/bin/env python | 
|  | # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | # Use of this source code is governed by a BSD-style license that can be | 
|  | # found in the LICENSE file. | 
|  |  | 
|  | """Module that sanitizes source files with specified modifiers.""" | 
|  |  | 
|  |  | 
|  | import commands | 
|  | import os | 
|  | import sys | 
|  |  | 
|  |  | 
|  | _FILE_EXTENSIONS_TO_SANITIZE = ['cpp', 'h', 'c', 'gyp', 'gypi'] | 
|  |  | 
|  | _SUBDIRS_TO_IGNORE = ['.git', '.svn', 'third_party'] | 
|  |  | 
|  |  | 
|  | def SanitizeFilesWithModifiers(directory, file_modifiers, line_modifiers): | 
|  | """Sanitizes source files with the specified file and line modifiers. | 
|  |  | 
|  | Args: | 
|  | directory: string - The directory which will be recursively traversed to | 
|  | find source files to apply modifiers to. | 
|  | file_modifiers: list - file-modification methods which should be applied to | 
|  | the complete file content (Eg: EOFOneAndOnlyOneNewlineAdder). | 
|  | line_modifiers: list - line-modification methods which should be applied to | 
|  | lines in a file (Eg: TabReplacer). | 
|  | """ | 
|  | for item in os.listdir(directory): | 
|  |  | 
|  | full_item_path = os.path.join(directory, item) | 
|  |  | 
|  | if os.path.isfile(full_item_path):  # Item is a file. | 
|  |  | 
|  | # Only sanitize files with extensions we care about. | 
|  | if (len(full_item_path.split('.')) > 1 and | 
|  | full_item_path.split('.')[-1] in _FILE_EXTENSIONS_TO_SANITIZE): | 
|  | f = file(full_item_path) | 
|  | try: | 
|  | lines = f.readlines() | 
|  | finally: | 
|  | f.close() | 
|  |  | 
|  | new_lines = []  # Collect changed lines here. | 
|  | line_number = 0  # Keeps track of line numbers in the source file. | 
|  | write_to_file = False  # File is written to only if this flag is set. | 
|  |  | 
|  | # Run the line modifiers for each line in this file. | 
|  | for line in lines: | 
|  | original_line = line | 
|  | line_number += 1 | 
|  |  | 
|  | for modifier in line_modifiers: | 
|  | line = modifier(line, full_item_path, line_number) | 
|  | if original_line != line: | 
|  | write_to_file = True | 
|  | new_lines.append(line) | 
|  |  | 
|  | # Run the file modifiers. | 
|  | old_content = ''.join(lines) | 
|  | new_content = ''.join(new_lines) | 
|  | for modifier in file_modifiers: | 
|  | new_content = modifier(new_content, full_item_path) | 
|  | if new_content != old_content: | 
|  | write_to_file = True | 
|  |  | 
|  | # Write modifications to the file. | 
|  | if write_to_file: | 
|  | f = file(full_item_path, 'w') | 
|  | try: | 
|  | f.write(new_content) | 
|  | finally: | 
|  | f.close() | 
|  | print 'Made changes to %s' % full_item_path | 
|  |  | 
|  | elif item not in _SUBDIRS_TO_IGNORE: | 
|  | # Item is a directory recursively call the method. | 
|  | SanitizeFilesWithModifiers(full_item_path, file_modifiers, line_modifiers) | 
|  |  | 
|  |  | 
|  | ############## Line Modification methods ############## | 
|  |  | 
|  |  | 
|  | def TrailingWhitespaceRemover(line, file_path, line_number): | 
|  | """Strips out trailing whitespaces from the specified line.""" | 
|  | stripped_line = line.rstrip() + '\n' | 
|  | if line != stripped_line: | 
|  | print 'Removing trailing whitespace in %s:%s' % (file_path, line_number) | 
|  | return stripped_line | 
|  |  | 
|  |  | 
|  | def CrlfReplacer(line, file_path, line_number): | 
|  | """Replaces CRLF with LF.""" | 
|  | if '\r\n' in line: | 
|  | print 'Replacing CRLF with LF in %s:%s' % (file_path, line_number) | 
|  | return line.replace('\r\n', '\n') | 
|  |  | 
|  |  | 
|  | def TabReplacer(line, file_path, line_number): | 
|  | """Replaces Tabs with 4 whitespaces.""" | 
|  | if '\t' in line: | 
|  | print 'Replacing Tab with whitespace in %s:%s' % (file_path, line_number) | 
|  | return line.replace('\t', '    ') | 
|  |  | 
|  |  | 
|  | ############## File Modification methods ############## | 
|  |  | 
|  |  | 
|  | def CopywriteChecker(file_content, unused_file_path): | 
|  | """Ensures that the copywrite information is correct.""" | 
|  | # TODO(rmistry): Figure out the legal implications of changing old copyright | 
|  | # headers. | 
|  | return file_content | 
|  |  | 
|  |  | 
|  | def EOFOneAndOnlyOneNewlineAdder(file_content, file_path): | 
|  | """Adds one and only one LF at the end of the file.""" | 
|  | if file_content and (file_content[-1] != '\n' or file_content[-2:-1] == '\n'): | 
|  | file_content = file_content.rstrip() | 
|  | file_content += '\n' | 
|  | print 'Added exactly one newline to %s' % file_path | 
|  | return file_content | 
|  |  | 
|  |  | 
|  | def SvnEOLChecker(file_content, file_path): | 
|  | """Sets svn:eol-style property to LF.""" | 
|  | output = commands.getoutput( | 
|  | 'svn propget svn:eol-style %s' % file_path) | 
|  | if output != 'LF': | 
|  | print 'Setting svn:eol-style property to LF in %s' % file_path | 
|  | os.system('svn ps svn:eol-style LF %s' % file_path) | 
|  | return file_content | 
|  |  | 
|  |  | 
|  | ####################################################### | 
|  |  | 
|  |  | 
|  | if '__main__' == __name__: | 
|  | sys.exit(SanitizeFilesWithModifiers( | 
|  | os.getcwd(), | 
|  | file_modifiers=[ | 
|  | CopywriteChecker, | 
|  | EOFOneAndOnlyOneNewlineAdder, | 
|  | SvnEOLChecker, | 
|  | ], | 
|  | line_modifiers=[ | 
|  | CrlfReplacer, | 
|  | TabReplacer, | 
|  | TrailingWhitespaceRemover, | 
|  | ], | 
|  | )) |