|
| 1 | +#! /usr/bin/env python |
| 2 | +## vim:set ts=4 sw=4 et: -*- coding: utf-8 -*- |
| 3 | +# |
| 4 | +# |
| 5 | +# Find all the whitespace format issues in the source |
| 6 | +# 1.) No trailing whitespace |
| 7 | +# 2.) No tab indentation |
| 8 | +# 3.) No CR/LF or CR lineendings |
| 9 | +# |
| 10 | +# sample run: |
| 11 | +# find .. -type f -name '*.cpp' -print0 -name '*.h' -print0 | xargs -r0 ./check_code_format.py |
| 12 | + |
| 13 | +import getopt, os, re, sys |
| 14 | + |
| 15 | +class opts: |
| 16 | + verbose = 1 |
| 17 | + recursive = False |
| 18 | + warn = False |
| 19 | + display_all = False # if true, display all lines with tab indent and trailing whitespace |
| 20 | + |
| 21 | + |
| 22 | +class globs: |
| 23 | + whitespace_on_line_end = re.compile("[ \t\f\v][\r\n]") |
| 24 | + tab_indent = re.compile("^\t", re.MULTILINE) |
| 25 | + crlf_lineend = re.compile("\r\n") # windows |
| 26 | + cr_lineend = re.compile("\r") # classicc MAC |
| 27 | + binary = re.compile("[\x00\x01\x02\xfe\xff]") # some binary |
| 28 | + |
| 29 | + |
| 30 | +# try to find a correct linenumber - even if lines are CR/LF, CR or LF terminated |
| 31 | +def determine_linenumber(data, pos): |
| 32 | + lineend = "\n" |
| 33 | + |
| 34 | + crlf = data.find("\r\n") |
| 35 | + if crlf != -1: |
| 36 | + lineend = "\r\n" |
| 37 | + else: |
| 38 | + cr = data.find("\r") |
| 39 | + if cr != -1: |
| 40 | + lineend = "\r" |
| 41 | + |
| 42 | + line = 1 |
| 43 | + p = 0 |
| 44 | + while True: |
| 45 | + p = data.find(lineend, p) |
| 46 | + if p == -1: |
| 47 | + return line |
| 48 | + if p >= pos: |
| 49 | + return line |
| 50 | + line += 1 |
| 51 | + p += len(lineend) |
| 52 | + |
| 53 | + |
| 54 | +def print_error(filename, data, match_objects, errormessage): |
| 55 | + linenumbers = [] |
| 56 | + #print match_objects, type(match_objects) |
| 57 | + for match in match_objects: |
| 58 | + linenumbers.append("%d" % determine_linenumber(data, match.start())) |
| 59 | + if not linenumbers: |
| 60 | + return |
| 61 | + if opts.warn: |
| 62 | + m = "Warning:" |
| 63 | + else: |
| 64 | + m = "Error:" |
| 65 | + print m, filename, errormessage + " (line %s)" % ",".join(linenumbers) |
| 66 | + # we could make an early exit here |
| 67 | + |
| 68 | + |
| 69 | +def do_one_file(filename): |
| 70 | + r = 0 |
| 71 | + if opts.verbose >= 3: |
| 72 | + print "checking", filename |
| 73 | + |
| 74 | + fp = open(filename, "rb") |
| 75 | + data = fp.read() |
| 76 | + fp.close() |
| 77 | + |
| 78 | + if globs.binary.search(data): |
| 79 | + if opts.verbose >= 2: |
| 80 | + print filename, " is a binary" |
| 81 | + return r |
| 82 | + |
| 83 | + # check for unix line endings (just \n) |
| 84 | + crlf = globs.crlf_lineend.search(data) |
| 85 | + cr = globs.cr_lineend.search(data) |
| 86 | + if crlf or cr: |
| 87 | + r = 1 |
| 88 | + if crlf and cr: |
| 89 | + if crlf.start() <= cr.start(): |
| 90 | + cr = None |
| 91 | + else: |
| 92 | + crlf = None |
| 93 | + if crlf: |
| 94 | + if opts.verbose > 0: |
| 95 | + print_error(filename, data, [crlf], "contains CR/LF linefeed") |
| 96 | + else: |
| 97 | + if opts.verbose > 0: |
| 98 | + print_error(filename, data, [cr], "contains CR linefeed") |
| 99 | + |
| 100 | + # TAB as indent |
| 101 | + if opts.display_all: |
| 102 | + tab_indent = globs.tab_indent.finditer(data) |
| 103 | + else: |
| 104 | + tab_indent = globs.tab_indent.search(data) |
| 105 | + if tab_indent: |
| 106 | + tab_indent = [tab_indent] |
| 107 | + if tab_indent: |
| 108 | + r = 1 |
| 109 | + if opts.verbose > 0: |
| 110 | + print_error(filename, data, tab_indent, "contains tab indent") |
| 111 | + |
| 112 | + # Whitespace before lineendings |
| 113 | + if opts.display_all: |
| 114 | + whitespace_on_line_end = globs.whitespace_on_line_end.finditer(data) |
| 115 | + else: |
| 116 | + whitespace_on_line_end = globs.whitespace_on_line_end.search(data) |
| 117 | + if whitespace_on_line_end: |
| 118 | + whitespace_on_line_end = [whitespace_on_line_end] |
| 119 | + if whitespace_on_line_end: |
| 120 | + if filename.endswith(".md"): |
| 121 | + pass # currently skip .md files |
| 122 | + else: |
| 123 | + r = 1 |
| 124 | + if opts.verbose > 0: |
| 125 | + print_error(filename, data, whitespace_on_line_end, "contains whitespaces at the line end") |
| 126 | + |
| 127 | + return r |
| 128 | + |
| 129 | + |
| 130 | +def do_one_directory(directory): |
| 131 | + r = 0 |
| 132 | + |
| 133 | + for root, dirs, files in os.walk(directory, topdown=True): |
| 134 | + for name in files: |
| 135 | + #print(os.path.join(root, name)) |
| 136 | + if do_one_file(os.path.join(root, name)): |
| 137 | + r = 1 |
| 138 | + if not opts.recursive: |
| 139 | + break |
| 140 | + return r |
| 141 | + |
| 142 | + |
| 143 | +def display_usage(argv): |
| 144 | + print "USAGE: %s [options] files/directories" % os.path.basename(argv[0]) |
| 145 | + print " -v, --verbose increase logging level" |
| 146 | + print " -q, --quiet decrease logging level" |
| 147 | + print " -h, --help display this page" |
| 148 | + print |
| 149 | + print " -a, --all display all lines with tab indent and trailing whitespace" |
| 150 | + print " -r, --recursive if given a directory check all recursive" |
| 151 | + print " -w, --warn just warn, don't exit with an error" |
| 152 | + |
| 153 | + |
| 154 | +def main(argv): |
| 155 | + try: assert 0 |
| 156 | + except AssertionError: pass |
| 157 | + else: raise Exception("fatal error - assertions not enabled") |
| 158 | + shortopts, longopts = "hqvarw", [ |
| 159 | + "help", "quiet","verbose", |
| 160 | + "all", "recusrive", "warn", |
| 161 | + ] |
| 162 | + xopts, args = getopt.gnu_getopt(argv[1:], shortopts, longopts) |
| 163 | + for opt, optarg in xopts: |
| 164 | + if opt in ["-q", "--quiet"]: |
| 165 | + opts.verbose = opts.verbose - 1 |
| 166 | + elif opt in ["-v", "--verbose"]: |
| 167 | + opts.verbose = opts.verbose + 1 |
| 168 | + elif opt in ["-h", "--help"]: |
| 169 | + display_usage(argv) |
| 170 | + return 0 |
| 171 | + elif opt in ["-a", "--all"]: |
| 172 | + opts.display_all = True |
| 173 | + elif opt in ["-r", "--recursive"]: |
| 174 | + opts.recursive = True |
| 175 | + elif opt in ["-w", "--warn"]: |
| 176 | + opts.warn = True |
| 177 | + |
| 178 | + if not args: |
| 179 | + r = do_one_directory(".") |
| 180 | + else: |
| 181 | + r = 0 |
| 182 | + for arg in args: |
| 183 | + if os.path.isdir(arg): |
| 184 | + if do_one_directory(arg): |
| 185 | + r = 1 |
| 186 | + else: |
| 187 | + if do_one_file(arg): |
| 188 | + r = 1 |
| 189 | + |
| 190 | + if opts.warn: |
| 191 | + return 0 |
| 192 | + return r |
| 193 | + |
| 194 | + |
| 195 | +if __name__ == "__main__": |
| 196 | + sys.exit(main(sys.argv)) |
0 commit comments