|  | #!/usr/bin/env python | 
|  | # Copyright (c) 2011 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. | 
|  |  | 
|  | """Find and fix files with inconsistent line endings. | 
|  |  | 
|  | This script requires 'dos2unix.exe' and 'unix2dos.exe' from Cygwin; they | 
|  | must be in the user's PATH. | 
|  |  | 
|  | Arg: Either one or more files to examine, or (with --file-list) one or more | 
|  | files that themselves contain lists of files. The argument(s) passed to | 
|  | this script, as well as the paths in the file if any, may be relative or | 
|  | absolute Windows-style paths (with either type of slash). The list might | 
|  | be generated with 'find -type f' or extracted from a gcl change listing, | 
|  | for example. | 
|  | """ | 
|  |  | 
|  | import errno | 
|  | import logging | 
|  | import optparse | 
|  | import subprocess | 
|  | import sys | 
|  |  | 
|  |  | 
|  | # Whether to produce excessive debugging output for each file in the list. | 
|  | DEBUGGING = False | 
|  |  | 
|  |  | 
|  | class Error(Exception): | 
|  | """Local exception class.""" | 
|  | pass | 
|  |  | 
|  |  | 
|  | def CountChars(text, str): | 
|  | """Count the number of instances of the given string in the text.""" | 
|  | split = text.split(str) | 
|  | logging.debug(len(split) - 1) | 
|  | return len(split) - 1 | 
|  |  | 
|  |  | 
|  | def PrevailingEOLName(crlf, cr, lf): | 
|  | """Describe the most common line ending. | 
|  |  | 
|  | Args: | 
|  | crlf: How many CRLF (\r\n) sequences are in the file. | 
|  | cr: How many CR (\r) characters are in the file, excluding CRLF sequences. | 
|  | lf: How many LF (\n) characters are in the file, excluding CRLF sequences. | 
|  |  | 
|  | Returns: | 
|  | A string describing the most common of the three line endings. | 
|  | """ | 
|  | most = max(crlf, cr, lf) | 
|  | if most == cr: | 
|  | return 'cr' | 
|  | if most == crlf: | 
|  | return 'crlf' | 
|  | return 'lf' | 
|  |  | 
|  |  | 
|  | def FixEndings(file, crlf, cr, lf): | 
|  | """Change the file's line endings to CRLF or LF, whichever is more common.""" | 
|  | most = max(crlf, cr, lf) | 
|  | if most == crlf: | 
|  | result = subprocess.call('unix2dos.exe %s' % file, shell=True) | 
|  | if result: | 
|  | raise Error('Error running unix2dos.exe %s' % file) | 
|  | else: | 
|  | result = subprocess.call('dos2unix.exe %s' % file, shell=True) | 
|  | if result: | 
|  | raise Error('Error running dos2unix.exe %s' % file) | 
|  |  | 
|  |  | 
|  | def ProcessFiles(filelist): | 
|  | """Fix line endings in each file in the filelist list.""" | 
|  | for filename in filelist: | 
|  | filename = filename.strip() | 
|  | logging.debug(filename) | 
|  | try: | 
|  | # Open in binary mode to preserve existing line endings. | 
|  | text = open(filename, 'rb').read() | 
|  | except IOError, e: | 
|  | if e.errno != errno.ENOENT: | 
|  | raise | 
|  | logging.warning('File %s not found.' % filename) | 
|  | continue | 
|  |  | 
|  | crlf = CountChars(text, '\r\n') | 
|  | cr = CountChars(text, '\r') - crlf | 
|  | lf = CountChars(text, '\n') - crlf | 
|  |  | 
|  | if options.force_lf: | 
|  | if crlf > 0 or cr > 0: | 
|  | print '%s: forcing to LF' % filename | 
|  | # Fudge the counts to force switching to LF. | 
|  | FixEndings(filename, 0, 0, 1) | 
|  | else: | 
|  | if ((crlf > 0 and cr > 0) or | 
|  | (crlf > 0 and lf > 0) or | 
|  | (  lf > 0 and cr > 0)): | 
|  | print '%s: mostly %s' % (filename, PrevailingEOLName(crlf, cr, lf)) | 
|  | FixEndings(filename, crlf, cr, lf) | 
|  |  | 
|  |  | 
|  | def process(options, args): | 
|  | """Process the files.""" | 
|  | if not args or len(args) < 1: | 
|  | raise Error('No files given.') | 
|  |  | 
|  | if options.file_list: | 
|  | for arg in args: | 
|  | filelist = open(arg, 'r').readlines() | 
|  | ProcessFiles(filelist) | 
|  | else: | 
|  | filelist = args | 
|  | ProcessFiles(filelist) | 
|  | return 0 | 
|  |  | 
|  |  | 
|  | def main(): | 
|  | if DEBUGGING: | 
|  | debug_level = logging.DEBUG | 
|  | else: | 
|  | debug_level = logging.INFO | 
|  | logging.basicConfig(level=debug_level, | 
|  | format='%(asctime)s %(levelname)-7s: %(message)s', | 
|  | datefmt='%H:%M:%S') | 
|  |  | 
|  | option_parser = optparse.OptionParser() | 
|  | option_parser.add_option("", "--file-list", action="store_true", | 
|  | default=False, | 
|  | help="Treat the arguments as files containing " | 
|  | "lists of files to examine, rather than as " | 
|  | "the files to be checked.") | 
|  | option_parser.add_option("", "--force-lf", action="store_true", | 
|  | default=False, | 
|  | help="Force any files with CRLF to LF instead.") | 
|  | options, args = option_parser.parse_args() | 
|  | return process(options, args) | 
|  |  | 
|  |  | 
|  | if '__main__' == __name__: | 
|  | sys.exit(main()) |