# This Python script checks directory and file names for case variation # when submitting files for add or branch. # See Perforce Technical Note #3 for more details of the problem # we are trying to prevent with this pre-submit trigger. # http://www.perforce.com/perforce/technotes/note003.html # This only works with Perforce server version 2002.2 or greater. # ( p4 describe doesn't return a list of file names in pending # changelists from earlier servers. ) # # Check case before submit for add or branch in a changelist. # Checks file names and directory path names. # # This is a recursive script. Perhaps a time saver for very large depots, # versus CheckC.pl since only directories or files that match are queried. # Could be slower for a small depot since more p4 commands issued compared # to CheckC.pl. # # Administrators can create depot names that differ in case but this script # will prevent users from adding files to the offending depot until the # problem is corrected. (That's assuming we're running checkcase from the # beginning of the Perforce server's existence.) # # As long as your Perforce server can locate the script you won't need the # p4port setting. This is left in for testing so you can run the script # remotely by supplying a changelist as an argument, e.g. # python checkcase.py 103 # # Gerry Thompson training Perforce 2002.2 version 1.0 import os, re, string, sys p4port = 'play:1667' # This is a Perforce server. p4user = 'gerry' # Perforce user name to use. p4client = 'none' # This script does not require a client spec. p4password = 'apassword' # If user has a password, include in p4 below. p4path = 'p4' # Path to p4 executable. p4 = p4path + ' -u'+ p4user + ' -p' + p4port # + ' -P ' + p4password + ' -c ' + p4client def nextlevel(subpath, upperpath, depot, next): next = next + 1 smallerpath = [] nextdepotpath = '' finished = 0 tempcandidate = '' blist = [] blistlower = [] # The depot path gets passed in unaltered. for singleline in os.popen(p4 + ' dirs "//' + depot + '/*"','r').readlines(): blist.append(singleline) blistlower.append(string.lower(singleline)) if len(blist) == 0: # This means we have new files and/or new directories. testpath = '"//' + depot + '/*"' getfile = re.compile('^info:.*'+depot+'/(.*)\#/?') reglist = [] reglistlower = [] # Get a list of files for comparison. for cline in os.popen(p4 + ' -s files ' + testpath, 'r').readlines(): if re.match('^info:.*', cline): regfile = getfile.search(cline).group(1) reglist.append(regfile) reglistlower.append(string.lower(regfile)) for j in range(len(subpath)): checkornot = string.split(subpath[j], '/' ) if len(checkornot) == next+1: # This means I have a file and not a new directory filename = string.split(subpath[j], '/')[-1] if reglistlower.count(filename) > 0: #print 'We have a regexp match' uppercasefile = string.split(upperpath[j], '/')[-1] if reglist.count(uppercasefile) == 0: print 'Error detected in file name case.' sys.exit(1) else: # Need to do some directory checking. for j in range(len(subpath)): if not finished: afile = subpath[j] anotherfile = upperpath[j] dirs = string.split(afile, '/') upperdirs = string.split(anotherfile, '/') if len(blistlower) > 0: # If blistlower is empty then we # have a new directory and I'm done # so finished gets set to 1. candidate = dirs[next] nextdepotpath = depot + '/' + upperdirs[next] checkdir = re.compile('^.*/'+candidate+'/?') tempdirs = filter(checkdir.search, blistlower) if len(tempdirs) > 0: newsmallpath = filter(checkdir.search, subpath) checkupperdir = re.compile('^.*/'+upperdirs[next]+'/?') casecheck = filter(checkupperdir.search, blist) if len(casecheck) == 0: # This means a lower case match was found # but not an upper case match. print 'Error detected in directory name case.' sys.exit(1) else: # Evaluate next set of directories. upperlist = filter(checkupperdir.search, upperpath) nextlevel(newsmallpath, upperlist, nextdepotpath, next) finished = 1 # No need to continue w/loop. else: finished = 1 def checkdepots(depotnames, lowerdepots, path, lowerpath): # Need to sort the list so the rest will go smoothly: path.sort() lowerpath.sort() next = 2 name = re.compile('^//(.+)/?') # Evaluate the depot path names to see if there is an error for i in range(len(lowerdepots)): depot = name.search(lowerdepots[i]).group(1) test = re.compile('//'+depot+'/') lower = filter(test.search, lowerpath) if len(lower) > 0: # Compare unaltered names, if one doesn't match, fail. depot = name.search(depotnames[i]).group(1) test = re.compile('//'+depot+'/') upper = filter(test.search, path) if len(lower) != len(upper): print 'Error detected in depot name case.' sys.exit(1) else: nextlevel(lower, upper, depot, next) def getlists(change): # Get a list of depots and files open for add or branch. depots = [] depotslower = [] # This list is the lowercased depot path. path = [] pathlower = [] # This list the the lowercased file path. checkoutput = re.compile('^.*(add|branch)') cleanup = re.compile('^....(//.*?)\#/?') # Error may be encountered if a remote depot is defined that # is off-line. for aline in os.popen(p4 + ' dirs "//*"','r').readlines(): # Quoting helps so the * doesn't get expanded locally. depots.append(aline) depotslower.append(string.lower(aline)) for bline in os.popen(p4 + ' describe ' + change,'r').readlines(): if checkoutput.match(bline): path.append(cleanup.search(bline).group(1)) pathlower.append(string.lower((cleanup.search(bline).group(1)))) if len(path) > 0: checkdepots(depots, depotslower, path, pathlower) if __name__ == '__main__': changelist = sys.argv[1] getlists(changelist)