| #!/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, | 
 |       ], | 
 |   )) |