inconsistent-eol.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. #!/usr/bin/env python
  2. # Copyright (c) 2011 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. """Find and fix files with inconsistent line endings.
  6. This script requires 'dos2unix.exe' and 'unix2dos.exe' from Cygwin; they
  7. must be in the user's PATH.
  8. Arg: Either one or more files to examine, or (with --file-list) one or more
  9. files that themselves contain lists of files. The argument(s) passed to
  10. this script, as well as the paths in the file if any, may be relative or
  11. absolute Windows-style paths (with either type of slash). The list might
  12. be generated with 'find -type f' or extracted from a gcl change listing,
  13. for example.
  14. """
  15. import errno
  16. import logging
  17. import optparse
  18. import subprocess
  19. import sys
  20. # Whether to produce excessive debugging output for each file in the list.
  21. DEBUGGING = False
  22. class Error(Exception):
  23. """Local exception class."""
  24. pass
  25. def CountChars(text, str):
  26. """Count the number of instances of the given string in the text."""
  27. split = text.split(str)
  28. logging.debug(len(split) - 1)
  29. return len(split) - 1
  30. def PrevailingEOLName(crlf, cr, lf):
  31. """Describe the most common line ending.
  32. Args:
  33. crlf: How many CRLF (\r\n) sequences are in the file.
  34. cr: How many CR (\r) characters are in the file, excluding CRLF sequences.
  35. lf: How many LF (\n) characters are in the file, excluding CRLF sequences.
  36. Returns:
  37. A string describing the most common of the three line endings.
  38. """
  39. most = max(crlf, cr, lf)
  40. if most == cr:
  41. return 'cr'
  42. if most == crlf:
  43. return 'crlf'
  44. return 'lf'
  45. def FixEndings(file, crlf, cr, lf):
  46. """Change the file's line endings to CRLF or LF, whichever is more common."""
  47. most = max(crlf, cr, lf)
  48. if most == crlf:
  49. result = subprocess.call('unix2dos.exe %s' % file, shell=True)
  50. if result:
  51. raise Error('Error running unix2dos.exe %s' % file)
  52. else:
  53. result = subprocess.call('dos2unix.exe %s' % file, shell=True)
  54. if result:
  55. raise Error('Error running dos2unix.exe %s' % file)
  56. def ProcessFiles(filelist):
  57. """Fix line endings in each file in the filelist list."""
  58. for filename in filelist:
  59. filename = filename.strip()
  60. logging.debug(filename)
  61. try:
  62. # Open in binary mode to preserve existing line endings.
  63. text = open(filename, 'rb').read()
  64. except IOError, e:
  65. if e.errno != errno.ENOENT:
  66. raise
  67. logging.warning('File %s not found.' % filename)
  68. continue
  69. crlf = CountChars(text, '\r\n')
  70. cr = CountChars(text, '\r') - crlf
  71. lf = CountChars(text, '\n') - crlf
  72. if options.force_lf:
  73. if crlf > 0 or cr > 0:
  74. print '%s: forcing to LF' % filename
  75. # Fudge the counts to force switching to LF.
  76. FixEndings(filename, 0, 0, 1)
  77. else:
  78. if ((crlf > 0 and cr > 0) or
  79. (crlf > 0 and lf > 0) or
  80. ( lf > 0 and cr > 0)):
  81. print '%s: mostly %s' % (filename, PrevailingEOLName(crlf, cr, lf))
  82. FixEndings(filename, crlf, cr, lf)
  83. def process(options, args):
  84. """Process the files."""
  85. if not args or len(args) < 1:
  86. raise Error('No files given.')
  87. if options.file_list:
  88. for arg in args:
  89. filelist = open(arg, 'r').readlines()
  90. ProcessFiles(filelist)
  91. else:
  92. filelist = args
  93. ProcessFiles(filelist)
  94. return 0
  95. def main():
  96. if DEBUGGING:
  97. debug_level = logging.DEBUG
  98. else:
  99. debug_level = logging.INFO
  100. logging.basicConfig(level=debug_level,
  101. format='%(asctime)s %(levelname)-7s: %(message)s',
  102. datefmt='%H:%M:%S')
  103. option_parser = optparse.OptionParser()
  104. option_parser.add_option("", "--file-list", action="store_true",
  105. default=False,
  106. help="Treat the arguments as files containing "
  107. "lists of files to examine, rather than as "
  108. "the files to be checked.")
  109. option_parser.add_option("", "--force-lf", action="store_true",
  110. default=False,
  111. help="Force any files with CRLF to LF instead.")
  112. options, args = option_parser.parse_args()
  113. return process(options, args)
  114. if '__main__' == __name__:
  115. sys.exit(main())