# Module 'dirmp' # # Defines a class to build directory diff tools on. import os import dircache import cmpcache import statcache from stat import * # Directory comparison class. # class dircmp: # def new(self, a, b): # Initialize self.a = a self.b = b # Properties that caller may change before calling self.run(): self.hide = [os.curdir, os.pardir] # Names never to be shown self.ignore = ['RCS', 'tags'] # Names ignored in comparison # return self # def run(self): # Compare everything except common subdirectories self.a_list = filter(dircache.listdir(self.a), self.hide) self.b_list = filter(dircache.listdir(self.b), self.hide) self.a_list.sort() self.b_list.sort() self.phase1() self.phase2() self.phase3() # def phase1(self): # Compute common names self.a_only = [] self.common = [] for x in self.a_list: if x in self.b_list: self.common.append(x) else: self.a_only.append(x) # self.b_only = [] for x in self.b_list: if x not in self.common: self.b_only.append(x) # def phase2(self): # Distinguish files, directories, funnies self.common_dirs = [] self.common_files = [] self.common_funny = [] # for x in self.common: a_path = os.path.join(self.a, x) b_path = os.path.join(self.b, x) # ok = 1 try: a_stat = statcache.stat(a_path) except os.error, why: # print 'Can\'t stat', a_path, ':', why[1] ok = 0 try: b_stat = statcache.stat(b_path) except os.error, why: # print 'Can\'t stat', b_path, ':', why[1] ok = 0 # if ok: a_type = S_IFMT(a_stat[ST_MODE]) b_type = S_IFMT(b_stat[ST_MODE]) if a_type <> b_type: self.common_funny.append(x) elif S_ISDIR(a_type): self.common_dirs.append(x) elif S_ISREG(a_type): self.common_files.append(x) else: self.common_funny.append(x) else: self.common_funny.append(x) # def phase3(self): # Find out differences between common files xx = cmpfiles(self.a, self.b, self.common_files) self.same_files, self.diff_files, self.funny_files = xx # def phase4(self): # Find out differences between common subdirectories # A new dircmp object is created for each common subdirectory, # these are stored in a dictionary indexed by filename. # The hide and ignore properties are inherited from the parent self.subdirs = {} for x in self.common_dirs: a_x = os.path.join(self.a, x) b_x = os.path.join(self.b, x) self.subdirs[x] = newdd = dircmp().new(a_x, b_x) newdd.hide = self.hide newdd.ignore = self.ignore newdd.run() # def phase4_closure(self): # Recursively call phase4() on subdirectories self.phase4() for x in self.subdirs.keys(): self.subdirs[x].phase4_closure() # def report(self): # Print a report on the differences between a and b # Assume that phases 1 to 3 have been executed # Output format is purposely lousy print 'diff', self.a, self.b if self.a_only: print 'Only in', self.a, ':', self.a_only if self.b_only: print 'Only in', self.b, ':', self.b_only if self.same_files: print 'Identical files :', self.same_files if self.diff_files: print 'Differing files :', self.diff_files if self.funny_files: print 'Trouble with common files :', self.funny_files if self.common_dirs: print 'Common subdirectories :', self.common_dirs if self.common_funny: print 'Common funny cases :', self.common_funny # def report_closure(self): # Print reports on self and on subdirs # If phase 4 hasn't been done, no subdir reports are printed self.report() try: x = self.subdirs except AttributeError: return # No subdirectories computed for x in self.subdirs.keys(): print self.subdirs[x].report_closure() # def report_phase4_closure(self): # Report and do phase 4 recursively self.report() self.phase4() for x in self.subdirs.keys(): print self.subdirs[x].report_phase4_closure() # Compare common files in two directories. # Return: # - files that compare equal # - files that compare different # - funny cases (can't stat etc.) # def cmpfiles(a, b, common): res = ([], [], []) for x in common: res[cmp(os.path.join(a, x), os.path.join(b, x))].append(x) return res # Compare two files. # Return: # 0 for equal # 1 for different # 2 for funny cases (can't stat, etc.) # def cmp(a, b): try: if cmpcache.cmp(a, b): return 0 return 1 except os.error: return 2 # Remove a list item. # NB: This modifies the list argument. # def remove(list, item): for i in range(len(list)): if list[i] == item: del list[i] break # Return a copy with items that occur in skip removed. # def filter(list, skip): result = [] for item in list: if item not in skip: result.append(item) return result # Demonstration and testing. # def demo(): import sys import getopt options, args = getopt.getopt(sys.argv[1:], 'r') if len(args) <> 2: raise getopt.error, 'need exactly two args' dd = dircmp().new(args[0], args[1]) dd.run() if ('-r', '') in options: dd.report_phase4_closure() else: dd.report() # demo()