13f770aabSAndy Fiddaman#!@TOOLS_PYTHON@ -Es 28bcea973SRichard Lowe# 38bcea973SRichard Lowe# This program is free software; you can redistribute it and/or modify 48bcea973SRichard Lowe# it under the terms of the GNU General Public License version 2 58bcea973SRichard Lowe# as published by the Free Software Foundation. 68bcea973SRichard Lowe# 78bcea973SRichard Lowe# This program is distributed in the hope that it will be useful, 88bcea973SRichard Lowe# but WITHOUT ANY WARRANTY; without even the implied warranty of 98bcea973SRichard Lowe# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 108bcea973SRichard Lowe# GNU General Public License for more details. 118bcea973SRichard Lowe# 128bcea973SRichard Lowe# You should have received a copy of the GNU General Public License 138bcea973SRichard Lowe# along with this program; if not, write to the Free Software 148bcea973SRichard Lowe# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 158bcea973SRichard Lowe# 168bcea973SRichard Lowe 178bcea973SRichard Lowe# 188bcea973SRichard Lowe# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 198bcea973SRichard Lowe# Copyright 2008, 2012 Richard Lowe 20955eb5e1SGarrett D'Amore# Copyright 2019 Garrett D'Amore <garrett@damore.org> 2193d2a904SPaul Dagnelie# Copyright (c) 2015, 2016 by Delphix. All rights reserved. 2228e2b3adSHans Rosenfeld# Copyright 2016 Nexenta Systems, Inc. 23972282a0SJohn Levon# Copyright (c) 2019, Joyent, Inc. 2413904da8SAndy Fiddaman# Copyright 2021 OmniOS Community Edition (OmniOSce) Association. 259af2fe54SBill Sommerfeld# Copyright 2024 Bill Sommerfeld 268bcea973SRichard Lowe# 278bcea973SRichard Lowe 28ca13eaa5SAndy Fiddamanfrom __future__ import print_function 29ca13eaa5SAndy Fiddaman 308bcea973SRichard Loweimport getopt 31ca13eaa5SAndy Fiddamanimport io 328bcea973SRichard Loweimport os 338bcea973SRichard Loweimport re 348bcea973SRichard Loweimport subprocess 358bcea973SRichard Loweimport sys 36ff50e8e5SRichard Loweimport tempfile 378bcea973SRichard Lowe 38ca13eaa5SAndy Fiddamanif sys.version_info[0] < 3: 398bcea973SRichard Lowe from cStringIO import StringIO 40ca13eaa5SAndy Fiddamanelse: 41ca13eaa5SAndy Fiddaman from io import StringIO 428bcea973SRichard Lowe 438bcea973SRichard Lowe# 448bcea973SRichard Lowe# Adjust the load path based on our location and the version of python into 458bcea973SRichard Lowe# which it is being loaded. This assumes the normal onbld directory 468bcea973SRichard Lowe# structure, where we are in bin/ and the modules are in 478bcea973SRichard Lowe# lib/python(version)?/onbld/Scm/. If that changes so too must this. 488bcea973SRichard Lowe# 498bcea973SRichard Lowesys.path.insert(1, os.path.join(os.path.dirname(__file__), "..", "lib", 508bcea973SRichard Lowe "python%d.%d" % sys.version_info[:2])) 518bcea973SRichard Lowe 528bcea973SRichard Lowe# 538bcea973SRichard Lowe# Add the relative path to usr/src/tools to the load path, such that when run 548bcea973SRichard Lowe# from the source tree we use the modules also within the source tree. 558bcea973SRichard Lowe# 568bcea973SRichard Lowesys.path.insert(2, os.path.join(os.path.dirname(__file__), "..")) 578bcea973SRichard Lowe 58e5587435SJoshua M. Clulowfrom onbld.Scm import Ignore 594ff15898SGordon Rossfrom onbld.Checks import Comments, Copyright, CStyle, HdrChk, WsCheck 6071af3be3SCody Peter Mellofrom onbld.Checks import JStyle, Keywords, ManLint, Mapfile, SpellCheck 6186d41711SAndy Fiddamanfrom onbld.Checks import ShellLint, PkgFmt 628bcea973SRichard Lowe 638bcea973SRichard Loweclass GitError(Exception): 648bcea973SRichard Lowe pass 658bcea973SRichard Lowe 668bcea973SRichard Lowedef git(command): 678bcea973SRichard Lowe """Run a command and return a stream containing its stdout (and write its 688bcea973SRichard Lowe stderr to its stdout)""" 698bcea973SRichard Lowe 708bcea973SRichard Lowe if type(command) != list: 718bcea973SRichard Lowe command = command.split() 728bcea973SRichard Lowe 738bcea973SRichard Lowe command = ["git"] + command 748bcea973SRichard Lowe 75ff50e8e5SRichard Lowe try: 76ca13eaa5SAndy Fiddaman tmpfile = tempfile.TemporaryFile(prefix="git-nits", mode="w+b") 77ca13eaa5SAndy Fiddaman except EnvironmentError as e: 78ff50e8e5SRichard Lowe raise GitError("Could not create temporary file: %s\n" % e) 79ff50e8e5SRichard Lowe 80ff50e8e5SRichard Lowe try: 818bcea973SRichard Lowe p = subprocess.Popen(command, 82ff50e8e5SRichard Lowe stdout=tmpfile, 83380fd671SMatthew Ahrens stderr=subprocess.PIPE) 84ca13eaa5SAndy Fiddaman except OSError as e: 85709afb1dSDillon Amburgey raise GitError("could not execute %s: %s\n" % (command, e)) 868bcea973SRichard Lowe 878bcea973SRichard Lowe err = p.wait() 888bcea973SRichard Lowe if err != 0: 89380fd671SMatthew Ahrens raise GitError(p.stderr.read()) 908bcea973SRichard Lowe 91ff50e8e5SRichard Lowe tmpfile.seek(0) 92ca13eaa5SAndy Fiddaman lines = [] 93ca13eaa5SAndy Fiddaman for l in tmpfile: 94ca13eaa5SAndy Fiddaman lines.append(l.decode('utf-8', 'replace')) 95ca13eaa5SAndy Fiddaman return lines 968bcea973SRichard Lowe 978bcea973SRichard Lowedef git_root(): 988bcea973SRichard Lowe """Return the root of the current git workspace""" 998bcea973SRichard Lowe 1009af2fe54SBill Sommerfeld p = git('rev-parse --show-toplevel') 1019af2fe54SBill Sommerfeld dir = p[0].strip() 1028bcea973SRichard Lowe 1039af2fe54SBill Sommerfeld return os.path.abspath(dir) 1048bcea973SRichard Lowe 1058bcea973SRichard Lowedef git_branch(): 1068bcea973SRichard Lowe """Return the current git branch""" 1078bcea973SRichard Lowe 1088bcea973SRichard Lowe p = git('branch') 1098bcea973SRichard Lowe 1108bcea973SRichard Lowe for elt in p: 1118bcea973SRichard Lowe if elt[0] == '*': 1128bcea973SRichard Lowe if elt.endswith('(no branch)'): 1138bcea973SRichard Lowe return None 1148bcea973SRichard Lowe return elt.split()[1] 1158bcea973SRichard Lowe 1168bcea973SRichard Lowedef git_parent_branch(branch): 1178bcea973SRichard Lowe """Return the parent of the current git branch. 1188bcea973SRichard Lowe 1198bcea973SRichard Lowe If this branch tracks a remote branch, return the remote branch which is 1208bcea973SRichard Lowe tracked. If not, default to origin/master.""" 1218bcea973SRichard Lowe 1228bcea973SRichard Lowe if not branch: 1238bcea973SRichard Lowe return None 1248bcea973SRichard Lowe 12528e2b3adSHans Rosenfeld p = git(["for-each-ref", "--format=%(refname:short) %(upstream:short)", 12628e2b3adSHans Rosenfeld "refs/heads/"]) 1278bcea973SRichard Lowe 1288bcea973SRichard Lowe if not p: 1298bcea973SRichard Lowe sys.stderr.write("Failed finding git parent branch\n") 130972282a0SJohn Levon sys.exit(1) 1318bcea973SRichard Lowe 1328bcea973SRichard Lowe for line in p: 1338bcea973SRichard Lowe # Git 1.7 will leave a ' ' trailing any non-tracking branch 1348bcea973SRichard Lowe if ' ' in line and not line.endswith(' \n'): 1358bcea973SRichard Lowe local, remote = line.split() 1368bcea973SRichard Lowe if local == branch: 1378bcea973SRichard Lowe return remote 1388bcea973SRichard Lowe return 'origin/master' 1398bcea973SRichard Lowe 140*3f8945a7SBill Sommerfelddef slices(strlist, sep): 141*3f8945a7SBill Sommerfeld """Yield start & end of each commit within the list of comments""" 142*3f8945a7SBill Sommerfeld low = 0 143*3f8945a7SBill Sommerfeld for i, v in enumerate(strlist): 144*3f8945a7SBill Sommerfeld if v == sep: 145*3f8945a7SBill Sommerfeld yield(low, i) 146*3f8945a7SBill Sommerfeld low = i+1 147*3f8945a7SBill Sommerfeld 148*3f8945a7SBill Sommerfeld if low != len(strlist): 149*3f8945a7SBill Sommerfeld yield(low, len(strlist)) 150*3f8945a7SBill Sommerfeld 1518bcea973SRichard Lowedef git_comments(parent): 152*3f8945a7SBill Sommerfeld """Return the checkin comments for each commit on this git branch, 153*3f8945a7SBill Sommerfeld structured as a list of lists of lines.""" 1548bcea973SRichard Lowe 15527495383SRichard Lowe p = git('log --pretty=tformat:%%B:SEP: %s..' % parent) 1568bcea973SRichard Lowe 1578bcea973SRichard Lowe if not p: 158972282a0SJohn Levon sys.stderr.write("No outgoing changesets found - missing -p option?\n"); 159972282a0SJohn Levon sys.exit(1) 1608bcea973SRichard Lowe 161*3f8945a7SBill Sommerfeld return [ [line.strip() for line in p[a:b]] 162*3f8945a7SBill Sommerfeld for (a, b) in slices(p, ':SEP:\n')] 1638bcea973SRichard Lowe 1648bcea973SRichard Lowedef git_file_list(parent, paths=None): 1658bcea973SRichard Lowe """Return the set of files which have ever changed on this branch. 1668bcea973SRichard Lowe 1678bcea973SRichard Lowe NB: This includes files which no longer exist, or no longer actually 1688bcea973SRichard Lowe differ.""" 1698bcea973SRichard Lowe 1708bcea973SRichard Lowe p = git("log --name-only --pretty=format: %s.. %s" % 1718bcea973SRichard Lowe (parent, ' '.join(paths))) 1728bcea973SRichard Lowe 1738bcea973SRichard Lowe if not p: 1748bcea973SRichard Lowe sys.stderr.write("Failed building file-list from git\n") 175972282a0SJohn Levon sys.exit(1) 1768bcea973SRichard Lowe 1778bcea973SRichard Lowe ret = set() 1788bcea973SRichard Lowe for fname in p: 179da88d39fSBill Sommerfeld fname = fname.strip() 180da88d39fSBill Sommerfeld if fname and not fname.isspace(): 181da88d39fSBill Sommerfeld ret.add(fname) 1828bcea973SRichard Lowe 183da88d39fSBill Sommerfeld return sorted(ret) 1848bcea973SRichard Lowe 1858bcea973SRichard Lowedef not_check(root, cmd): 1868bcea973SRichard Lowe """Return a function which returns True if a file given as an argument 1878bcea973SRichard Lowe should be excluded from the check named by 'cmd'""" 1888bcea973SRichard Lowe 189ca13eaa5SAndy Fiddaman ignorefiles = list(filter(os.path.exists, 190c82c4676SGordon Ross [os.path.join(root, ".git/info", "%s.NOT" % cmd), 191ca13eaa5SAndy Fiddaman os.path.join(root, "exception_lists", cmd)])) 192e5587435SJoshua M. Clulow return Ignore.ignore(root, ignorefiles) 1938bcea973SRichard Lowe 194955eb5e1SGarrett D'Amoredef gen_files(root, parent, paths, exclude, filter=None): 1958bcea973SRichard Lowe """Return a function producing file names, relative to the current 1968bcea973SRichard Lowe directory, of any file changed on this branch (limited to 'paths' if 1978bcea973SRichard Lowe requested), and excluding files for which exclude returns a true value """ 1988bcea973SRichard Lowe 199955eb5e1SGarrett D'Amore if filter is None: 200955eb5e1SGarrett D'Amore filter = lambda x: os.path.isfile(x) 201955eb5e1SGarrett D'Amore 2028bcea973SRichard Lowe def ret(select=None): 2038bcea973SRichard Lowe if not select: 2048bcea973SRichard Lowe select = lambda x: True 2058bcea973SRichard Lowe 20638e36c53SJohn Levon for abspath in git_file_list(parent, paths): 2079af2fe54SBill Sommerfeld path = os.path.relpath(os.path.join(root, abspath), '.') 20893d2a904SPaul Dagnelie try: 20938e36c53SJohn Levon res = git("diff %s HEAD %s" % (parent, path)) 210ca13eaa5SAndy Fiddaman except GitError as e: 21138e36c53SJohn Levon # This ignores all the errors that can be thrown. Usually, this 21238e36c53SJohn Levon # means that git returned non-zero because the file doesn't 21338e36c53SJohn Levon # exist, but it could also fail if git can't create a new file 21438e36c53SJohn Levon # or it can't be executed. Such errors are 1) unlikely, and 2) 21538e36c53SJohn Levon # will be caught by other invocations of git(). 21693d2a904SPaul Dagnelie continue 217ca13eaa5SAndy Fiddaman empty = not res 218955eb5e1SGarrett D'Amore if (filter(path) and not empty and 21938e36c53SJohn Levon select(path) and not exclude(abspath)): 22038e36c53SJohn Levon yield path 2218bcea973SRichard Lowe return ret 2228bcea973SRichard Lowe 223955eb5e1SGarrett D'Amoredef gen_links(root, parent, paths, exclude): 224955eb5e1SGarrett D'Amore """Return a function producing symbolic link names, relative to the current 225955eb5e1SGarrett D'Amore directory, of any file changed on this branch (limited to 'paths' if 226955eb5e1SGarrett D'Amore requested), and excluding files for which exclude returns a true value """ 227955eb5e1SGarrett D'Amore 228955eb5e1SGarrett D'Amore return gen_files(root, parent, paths, exclude, lambda x: os.path.islink(x)) 229955eb5e1SGarrett D'Amore 2308bcea973SRichard Lowedef comchk(root, parent, flist, output): 2318bcea973SRichard Lowe output.write("Comments:\n") 2328bcea973SRichard Lowe 2338d226a82SBill Sommerfeld comments = git_comments(parent) 234*3f8945a7SBill Sommerfeld multi = len(comments) > 1 235*3f8945a7SBill Sommerfeld state = {} 2368d226a82SBill Sommerfeld 237*3f8945a7SBill Sommerfeld ret = 0 238*3f8945a7SBill Sommerfeld for commit in comments: 2398bcea973SRichard Lowe 240*3f8945a7SBill Sommerfeld s = StringIO() 241*3f8945a7SBill Sommerfeld 242*3f8945a7SBill Sommerfeld result = Comments.comchk(commit, check_db=True, 243*3f8945a7SBill Sommerfeld output=s, bugs=state) 244*3f8945a7SBill Sommerfeld ret |= result 245*3f8945a7SBill Sommerfeld 246*3f8945a7SBill Sommerfeld if result != 0: 247*3f8945a7SBill Sommerfeld if multi: 248*3f8945a7SBill Sommerfeld output.write('\n%s\n' % commit[0]) 249*3f8945a7SBill Sommerfeld output.write(s.getvalue()) 250*3f8945a7SBill Sommerfeld 251*3f8945a7SBill Sommerfeld return ret 2528bcea973SRichard Lowe 2538bcea973SRichard Lowedef mapfilechk(root, parent, flist, output): 2548bcea973SRichard Lowe ret = 0 2558bcea973SRichard Lowe 2568bcea973SRichard Lowe # We are interested in examining any file that has the following 2578bcea973SRichard Lowe # in its final path segment: 2588bcea973SRichard Lowe # - Contains the word 'mapfile' 2598bcea973SRichard Lowe # - Begins with 'map.' 2608bcea973SRichard Lowe # - Ends with '.map' 2618bcea973SRichard Lowe # We don't want to match unless these things occur in final path segment 2628bcea973SRichard Lowe # because directory names with these strings don't indicate a mapfile. 2638bcea973SRichard Lowe # We also ignore files with suffixes that tell us that the files 2648bcea973SRichard Lowe # are not mapfiles. 2658bcea973SRichard Lowe MapfileRE = re.compile(r'.*((mapfile[^/]*)|(/map\.+[^/]*)|(\.map))$', 2668bcea973SRichard Lowe re.IGNORECASE) 2678bcea973SRichard Lowe NotMapSuffixRE = re.compile(r'.*\.[ch]$', re.IGNORECASE) 2688bcea973SRichard Lowe 2698bcea973SRichard Lowe output.write("Mapfile comments:\n") 2708bcea973SRichard Lowe 2718bcea973SRichard Lowe for f in flist(lambda x: MapfileRE.match(x) and not 2728bcea973SRichard Lowe NotMapSuffixRE.match(x)): 273ca13eaa5SAndy Fiddaman with io.open(f, encoding='utf-8', errors='replace') as fh: 2748bcea973SRichard Lowe ret |= Mapfile.mapfilechk(fh, output=output) 2758bcea973SRichard Lowe return ret 2768bcea973SRichard Lowe 2778bcea973SRichard Lowedef copyright(root, parent, flist, output): 2788bcea973SRichard Lowe ret = 0 2798bcea973SRichard Lowe output.write("Copyrights:\n") 2808bcea973SRichard Lowe for f in flist(): 281ca13eaa5SAndy Fiddaman with io.open(f, encoding='utf-8', errors='replace') as fh: 2828bcea973SRichard Lowe ret |= Copyright.copyright(fh, output=output) 2838bcea973SRichard Lowe return ret 2848bcea973SRichard Lowe 2858bcea973SRichard Lowedef hdrchk(root, parent, flist, output): 2868bcea973SRichard Lowe ret = 0 2878bcea973SRichard Lowe output.write("Header format:\n") 2888bcea973SRichard Lowe for f in flist(lambda x: x.endswith('.h')): 289ca13eaa5SAndy Fiddaman with io.open(f, encoding='utf-8', errors='replace') as fh: 2908bcea973SRichard Lowe ret |= HdrChk.hdrchk(fh, lenient=True, output=output) 2918bcea973SRichard Lowe return ret 2928bcea973SRichard Lowe 2938bcea973SRichard Lowedef cstyle(root, parent, flist, output): 2948bcea973SRichard Lowe ret = 0 2958bcea973SRichard Lowe output.write("C style:\n") 2968bcea973SRichard Lowe for f in flist(lambda x: x.endswith('.c') or x.endswith('.h')): 297a90997d2SAndy Fiddaman with io.open(f, mode='rb') as fh: 2988bcea973SRichard Lowe ret |= CStyle.cstyle(fh, output=output, picky=True, 2998bcea973SRichard Lowe check_posix_types=True, 3008bcea973SRichard Lowe check_continuation=True) 3018bcea973SRichard Lowe return ret 3028bcea973SRichard Lowe 3038bcea973SRichard Lowedef jstyle(root, parent, flist, output): 3048bcea973SRichard Lowe ret = 0 3058bcea973SRichard Lowe output.write("Java style:\n") 3068bcea973SRichard Lowe for f in flist(lambda x: x.endswith('.java')): 30710811751SAndy Fiddaman with io.open(f, mode='rb') as fh: 3088bcea973SRichard Lowe ret |= JStyle.jstyle(fh, output=output, picky=True) 3098bcea973SRichard Lowe return ret 3108bcea973SRichard Lowe 31195c635efSGarrett D'Amoredef manlint(root, parent, flist, output): 31295c635efSGarrett D'Amore ret = 0 31371af3be3SCody Peter Mello output.write("Man page format/spelling:\n") 31495c635efSGarrett D'Amore ManfileRE = re.compile(r'.*\.[0-9][a-z]*$', re.IGNORECASE) 31595c635efSGarrett D'Amore for f in flist(lambda x: ManfileRE.match(x)): 316a90997d2SAndy Fiddaman with io.open(f, mode='rb') as fh: 31795c635efSGarrett D'Amore ret |= ManLint.manlint(fh, output=output, picky=True) 31871af3be3SCody Peter Mello ret |= SpellCheck.spellcheck(fh, output=output) 31995c635efSGarrett D'Amore return ret 32095c635efSGarrett D'Amore 32113904da8SAndy Fiddamandef shelllint(root, parent, flist, output): 32213904da8SAndy Fiddaman ret = 0 32313904da8SAndy Fiddaman output.write("Shell lint:\n") 32413904da8SAndy Fiddaman 32513904da8SAndy Fiddaman def isshell(x): 32613904da8SAndy Fiddaman (_, ext) = os.path.splitext(x) 32713904da8SAndy Fiddaman if ext in ['.sh', '.ksh']: 32813904da8SAndy Fiddaman return True 32913904da8SAndy Fiddaman if ext == '': 33013904da8SAndy Fiddaman with io.open(x, mode='r', errors='ignore') as fh: 33113904da8SAndy Fiddaman if re.match(r'^#.*\bk?sh\b', fh.readline()): 33213904da8SAndy Fiddaman return True 33313904da8SAndy Fiddaman return False 33413904da8SAndy Fiddaman 33513904da8SAndy Fiddaman for f in flist(isshell): 33613904da8SAndy Fiddaman with io.open(f, mode='rb') as fh: 33713904da8SAndy Fiddaman ret |= ShellLint.lint(fh, output=output) 33813904da8SAndy Fiddaman 33913904da8SAndy Fiddaman return ret 34013904da8SAndy Fiddaman 34186d41711SAndy Fiddamandef pkgfmt(root, parent, flist, output): 34286d41711SAndy Fiddaman ret = 0 34386d41711SAndy Fiddaman output.write("Package manifests:\n") 34486d41711SAndy Fiddaman 34525b05a3eSAndy Fiddaman for f in flist(lambda x: x.endswith('.p5m')): 34686d41711SAndy Fiddaman with io.open(f, mode='rb') as fh: 34786d41711SAndy Fiddaman ret |= PkgFmt.check(fh, output=output) 34886d41711SAndy Fiddaman 34986d41711SAndy Fiddaman return ret 35086d41711SAndy Fiddaman 3518bcea973SRichard Lowedef keywords(root, parent, flist, output): 3528bcea973SRichard Lowe ret = 0 3538bcea973SRichard Lowe output.write("SCCS Keywords:\n") 3548bcea973SRichard Lowe for f in flist(): 355ca13eaa5SAndy Fiddaman with io.open(f, encoding='utf-8', errors='replace') as fh: 3568bcea973SRichard Lowe ret |= Keywords.keywords(fh, output=output) 3578bcea973SRichard Lowe return ret 3588bcea973SRichard Lowe 3594ff15898SGordon Rossdef wscheck(root, parent, flist, output): 3604ff15898SGordon Ross ret = 0 3614ff15898SGordon Ross output.write("white space nits:\n") 3624ff15898SGordon Ross for f in flist(): 363ca13eaa5SAndy Fiddaman with io.open(f, encoding='utf-8', errors='replace') as fh: 3644ff15898SGordon Ross ret |= WsCheck.wscheck(fh, output=output) 3654ff15898SGordon Ross return ret 3668bcea973SRichard Lowe 367955eb5e1SGarrett D'Amoredef symlinks(root, parent, flist, output): 368955eb5e1SGarrett D'Amore ret = 0 369955eb5e1SGarrett D'Amore output.write("Symbolic links:\n") 370955eb5e1SGarrett D'Amore for f in flist(): 371955eb5e1SGarrett D'Amore output.write(" "+f+"\n") 372955eb5e1SGarrett D'Amore ret |= 1 373955eb5e1SGarrett D'Amore return ret 374955eb5e1SGarrett D'Amore 375955eb5e1SGarrett D'Amoredef iswinreserved(name): 376955eb5e1SGarrett D'Amore reserved = [ 377955eb5e1SGarrett D'Amore 'con', 'prn', 'aux', 'nul', 378955eb5e1SGarrett D'Amore 'com1', 'com2', 'com3', 'com4', 'com5', 379955eb5e1SGarrett D'Amore 'com6', 'com7', 'com8', 'com9', 'com0', 380955eb5e1SGarrett D'Amore 'lpt1', 'lpt2', 'lpt3', 'lpt4', 'lpt5', 381955eb5e1SGarrett D'Amore 'lpt6', 'lpt7', 'lpt8', 'lpt9', 'lpt0' ] 382955eb5e1SGarrett D'Amore l = name.lower() 383955eb5e1SGarrett D'Amore for r in reserved: 384955eb5e1SGarrett D'Amore if l == r or l.startswith(r+"."): 385955eb5e1SGarrett D'Amore return True 386955eb5e1SGarrett D'Amore return False 387955eb5e1SGarrett D'Amore 388955eb5e1SGarrett D'Amoredef haswinspecial(name): 389955eb5e1SGarrett D'Amore specials = '<>:"\\|?*' 390955eb5e1SGarrett D'Amore for c in name: 391955eb5e1SGarrett D'Amore if c in specials: 392955eb5e1SGarrett D'Amore return True 393955eb5e1SGarrett D'Amore return False 394955eb5e1SGarrett D'Amore 395955eb5e1SGarrett D'Amoredef winnames(root, parent, flist, output): 396955eb5e1SGarrett D'Amore ret = 0 397955eb5e1SGarrett D'Amore output.write("Illegal filenames (Windows):\n") 398955eb5e1SGarrett D'Amore for f in flist(): 399955eb5e1SGarrett D'Amore if haswinspecial(f): 400955eb5e1SGarrett D'Amore output.write(" "+f+": invalid character in name\n") 401955eb5e1SGarrett D'Amore ret |= 1 402955eb5e1SGarrett D'Amore continue 403955eb5e1SGarrett D'Amore 404955eb5e1SGarrett D'Amore parts = f.split('/') 405955eb5e1SGarrett D'Amore for p in parts: 406955eb5e1SGarrett D'Amore if iswinreserved(p): 407955eb5e1SGarrett D'Amore output.write(" "+f+": reserved file name\n") 408955eb5e1SGarrett D'Amore ret |= 1 409955eb5e1SGarrett D'Amore break 410955eb5e1SGarrett D'Amore 411955eb5e1SGarrett D'Amore return ret 412955eb5e1SGarrett D'Amore 413955eb5e1SGarrett D'Amoredef run_checks(root, parent, cmds, scmds, paths='', opts={}): 4148bcea973SRichard Lowe """Run the checks given in 'cmds', expected to have well-known signatures, 4158bcea973SRichard Lowe and report results for any which fail. 4168bcea973SRichard Lowe 4178bcea973SRichard Lowe Return failure if any of them did. 4188bcea973SRichard Lowe 4198bcea973SRichard Lowe NB: the function name of the commands passed in is used to name the NOT 4208bcea973SRichard Lowe file which excepts files from them.""" 4218bcea973SRichard Lowe 4228bcea973SRichard Lowe ret = 0 4238bcea973SRichard Lowe 4248bcea973SRichard Lowe for cmd in cmds: 4258bcea973SRichard Lowe s = StringIO() 4268bcea973SRichard Lowe 427ca13eaa5SAndy Fiddaman exclude = not_check(root, cmd.__name__) 4288bcea973SRichard Lowe result = cmd(root, parent, gen_files(root, parent, paths, exclude), 4298bcea973SRichard Lowe output=s) 4308bcea973SRichard Lowe ret |= result 4318bcea973SRichard Lowe 4328bcea973SRichard Lowe if result != 0: 433ca13eaa5SAndy Fiddaman print(s.getvalue()) 4348bcea973SRichard Lowe 435955eb5e1SGarrett D'Amore for cmd in scmds: 436955eb5e1SGarrett D'Amore s = StringIO() 437955eb5e1SGarrett D'Amore 438955eb5e1SGarrett D'Amore exclude = not_check(root, cmd.__name__) 439955eb5e1SGarrett D'Amore result = cmd(root, parent, gen_links(root, parent, paths, exclude), 440955eb5e1SGarrett D'Amore output=s) 441955eb5e1SGarrett D'Amore ret |= result 442955eb5e1SGarrett D'Amore 443955eb5e1SGarrett D'Amore if result != 0: 444955eb5e1SGarrett D'Amore print(s.getvalue()) 445955eb5e1SGarrett D'Amore 4468bcea973SRichard Lowe return ret 4478bcea973SRichard Lowe 4488bcea973SRichard Lowedef nits(root, parent, paths): 4498bcea973SRichard Lowe cmds = [copyright, 4508bcea973SRichard Lowe cstyle, 4518bcea973SRichard Lowe hdrchk, 4528bcea973SRichard Lowe jstyle, 4538bcea973SRichard Lowe keywords, 45495c635efSGarrett D'Amore manlint, 4554ff15898SGordon Ross mapfilechk, 45613904da8SAndy Fiddaman shelllint, 45786d41711SAndy Fiddaman pkgfmt, 458955eb5e1SGarrett D'Amore winnames, 4594ff15898SGordon Ross wscheck] 460955eb5e1SGarrett D'Amore scmds = [symlinks] 461955eb5e1SGarrett D'Amore run_checks(root, parent, cmds, scmds, paths) 4628bcea973SRichard Lowe 4638bcea973SRichard Lowedef pbchk(root, parent, paths): 4648bcea973SRichard Lowe cmds = [comchk, 4658bcea973SRichard Lowe copyright, 4668bcea973SRichard Lowe cstyle, 4678bcea973SRichard Lowe hdrchk, 4688bcea973SRichard Lowe jstyle, 4698bcea973SRichard Lowe keywords, 47095c635efSGarrett D'Amore manlint, 4714ff15898SGordon Ross mapfilechk, 47213904da8SAndy Fiddaman shelllint, 47386d41711SAndy Fiddaman pkgfmt, 474955eb5e1SGarrett D'Amore winnames, 4754ff15898SGordon Ross wscheck] 476955eb5e1SGarrett D'Amore scmds = [symlinks] 477955eb5e1SGarrett D'Amore run_checks(root, parent, cmds, scmds) 4788bcea973SRichard Lowe 4798bcea973SRichard Lowedef main(cmd, args): 4808bcea973SRichard Lowe parent_branch = None 481eabe844aSJohn Levon checkname = None 4828bcea973SRichard Lowe 4838bcea973SRichard Lowe try: 48442a3762dSJoshua M. Clulow opts, args = getopt.getopt(args, 'b:c:p:') 485ca13eaa5SAndy Fiddaman except getopt.GetoptError as e: 4868bcea973SRichard Lowe sys.stderr.write(str(e) + '\n') 487eabe844aSJohn Levon sys.stderr.write("Usage: %s [-c check] [-p branch] [path...]\n" % cmd) 4888bcea973SRichard Lowe sys.exit(1) 4898bcea973SRichard Lowe 4908bcea973SRichard Lowe for opt, arg in opts: 49142a3762dSJoshua M. Clulow # We accept "-b" as an alias of "-p" for backwards compatibility. 49242a3762dSJoshua M. Clulow if opt == '-p' or opt == '-b': 4938bcea973SRichard Lowe parent_branch = arg 494eabe844aSJohn Levon elif opt == '-c': 495eabe844aSJohn Levon checkname = arg 4968bcea973SRichard Lowe 4978bcea973SRichard Lowe if not parent_branch: 4988bcea973SRichard Lowe parent_branch = git_parent_branch(git_branch()) 4998bcea973SRichard Lowe 500eabe844aSJohn Levon if checkname is None: 5018bcea973SRichard Lowe if cmd == 'git-pbchk': 502eabe844aSJohn Levon checkname = 'pbchk' 503eabe844aSJohn Levon else: 504eabe844aSJohn Levon checkname = 'nits' 505eabe844aSJohn Levon 506eabe844aSJohn Levon if checkname == 'pbchk': 5078bcea973SRichard Lowe if args: 5088bcea973SRichard Lowe sys.stderr.write("only complete workspaces may be pbchk'd\n"); 5098bcea973SRichard Lowe sys.exit(1) 510eabe844aSJohn Levon pbchk(git_root(), parent_branch, None) 511eabe844aSJohn Levon elif checkname == 'nits': 512eabe844aSJohn Levon nits(git_root(), parent_branch, args) 513eabe844aSJohn Levon else: 514eabe844aSJohn Levon run_checks(git_root(), parent_branch, [eval(checkname)], args) 5158bcea973SRichard Lowe 5168bcea973SRichard Loweif __name__ == '__main__': 5178bcea973SRichard Lowe try: 5188bcea973SRichard Lowe main(os.path.basename(sys.argv[0]), sys.argv[1:]) 519ca13eaa5SAndy Fiddaman except GitError as e: 5208bcea973SRichard Lowe sys.stderr.write("failed to run git:\n %s\n" % str(e)) 5218bcea973SRichard Lowe sys.exit(1) 522