The script does the following:
* Adds a newline to the end of files if it does not have one.
* Sets svn:eol-style to LF.
* Replaces CRLF with LF.
* Replaces Tabs with 4 whitespaces.
* Removes trailing whitespaces.

Created to fix http://code.google.com/p/skia/issues/detail?id=779 : New buildbot step that fixes and submits whitespace and newline source file fixes.
Review URL: https://codereview.appspot.com/6465078

git-svn-id: http://skia.googlecode.com/svn/trunk@5234 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/tools/sanitize_source_files.py b/tools/sanitize_source_files.py
new file mode 100755
index 0000000..26998e2
--- /dev/null
+++ b/tools/sanitize_source_files.py
@@ -0,0 +1,152 @@
+#!/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 = ['.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: EOFNewlineAdder).
+    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 EOFNewlineAdder(file_content, file_path):
+  """Adds a LF at the end of the file if it does not have one."""
+  if file_content and file_content[-1] == '\n':
+    file_content += '\n'
+    print 'Added 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,
+          EOFNewlineAdder,
+          SvnEOLChecker,
+      ],
+      line_modifiers=[
+          CrlfReplacer,
+          TabReplacer,
+          TrailingWhitespaceRemover,
+      ],
+  ))
+