mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 19:34:08 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			205 lines
		
	
	
	
		
			4.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			205 lines
		
	
	
	
		
			4.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# Module 'dirmp'
 | 
						|
#
 | 
						|
# Defines a class to build directory diff tools on.
 | 
						|
 | 
						|
import posix
 | 
						|
 | 
						|
import path
 | 
						|
 | 
						|
import dircache
 | 
						|
import cmpcache
 | 
						|
import statcache
 | 
						|
from stat import *
 | 
						|
 | 
						|
# Directory comparison class.
 | 
						|
#
 | 
						|
class dircmp:
 | 
						|
	#
 | 
						|
	def new(dd, (a, b)): # Initialize
 | 
						|
		dd.a = a
 | 
						|
		dd.b = b
 | 
						|
		# Properties that caller may change before callingdd. run():
 | 
						|
		dd.hide = ['.', '..'] # Names never to be shown
 | 
						|
		dd.ignore = ['RCS', 'tags'] # Names ignored in comparison
 | 
						|
		#
 | 
						|
		return dd
 | 
						|
	#
 | 
						|
	def run(dd): # Compare everything except common subdirectories
 | 
						|
		dd.a_list = filter(dircache.listdir(dd.a), dd.hide)
 | 
						|
		dd.b_list = filter(dircache.listdir(dd.b), dd.hide)
 | 
						|
		dd.a_list.sort()
 | 
						|
		dd.b_list.sort()
 | 
						|
		dd.phase1()
 | 
						|
		dd.phase2()
 | 
						|
		dd.phase3()
 | 
						|
	#
 | 
						|
	def phase1(dd): # Compute common names
 | 
						|
		dd.a_only = []
 | 
						|
		dd.common = []
 | 
						|
		for x in dd.a_list:
 | 
						|
			if x in dd.b_list:
 | 
						|
				dd.common.append(x)
 | 
						|
			else:
 | 
						|
				dd.a_only.append(x)
 | 
						|
		#
 | 
						|
		dd.b_only = []
 | 
						|
		for x in dd.b_list:
 | 
						|
			if x not in dd.common:
 | 
						|
				dd.b_only.append(x)
 | 
						|
	#
 | 
						|
	def phase2(dd): # Distinguish files, directories, funnies
 | 
						|
		dd.common_dirs = []
 | 
						|
		dd.common_files = []
 | 
						|
		dd.common_funny = []
 | 
						|
		#
 | 
						|
		for x in dd.common:
 | 
						|
			a_path = path.join(dd.a, x)
 | 
						|
			b_path = path.join(dd.b, x)
 | 
						|
			#
 | 
						|
			ok = 1
 | 
						|
			try:
 | 
						|
				a_stat = statcache.stat(a_path)
 | 
						|
			except posix.error, why:
 | 
						|
				# print 'Can\'t stat', a_path, ':', why[1]
 | 
						|
				ok = 0
 | 
						|
			try:
 | 
						|
				b_stat = statcache.stat(b_path)
 | 
						|
			except posix.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:
 | 
						|
					dd.common_funny.append(x)
 | 
						|
				elif S_ISDIR(a_type):
 | 
						|
					dd.common_dirs.append(x)
 | 
						|
				elif S_ISREG(a_type):
 | 
						|
					dd.common_files.append(x)
 | 
						|
				else:
 | 
						|
					dd.common_funny.append(x)
 | 
						|
			else:
 | 
						|
				dd.common_funny.append(x)
 | 
						|
	#
 | 
						|
	def phase3(dd): # Find out differences between common files
 | 
						|
		xx = cmpfiles(dd.a, dd.b, dd.common_files)
 | 
						|
		dd.same_files, dd.diff_files, dd.funny_files = xx
 | 
						|
	#
 | 
						|
	def phase4(dd): # 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
 | 
						|
		dd.subdirs = {}
 | 
						|
		for x in dd.common_dirs:
 | 
						|
			a_x = path.join(dd.a, x)
 | 
						|
			b_x = path.join(dd.b, x)
 | 
						|
			dd.subdirs[x] = newdd = dircmp().new(a_x, b_x)
 | 
						|
			newdd.hide = dd.hide
 | 
						|
			newdd.ignore = dd.ignore
 | 
						|
			newdd.run()
 | 
						|
	#
 | 
						|
	def phase4_closure(dd): # Recursively call phase4() on subdirectories
 | 
						|
		dd.phase4()
 | 
						|
		for x in dd.subdirs.keys():
 | 
						|
			dd.subdirs[x].phase4_closure()
 | 
						|
	#
 | 
						|
	def report(dd): # 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', dd.a, dd.b
 | 
						|
		if dd.a_only:
 | 
						|
			print 'Only in', dd.a, ':', dd.a_only
 | 
						|
		if dd.b_only:
 | 
						|
			print 'Only in', dd.b, ':', dd.b_only
 | 
						|
		if dd.same_files:
 | 
						|
			print 'Identical files :', dd.same_files
 | 
						|
		if dd.diff_files:
 | 
						|
			print 'Differing files :', dd.diff_files
 | 
						|
		if dd.funny_files:
 | 
						|
			print 'Trouble with common files :', dd.funny_files
 | 
						|
		if dd.common_dirs:
 | 
						|
			print 'Common subdirectories :', dd.common_dirs
 | 
						|
		if dd.common_funny:
 | 
						|
			print 'Common funny cases :', dd.common_funny
 | 
						|
	#
 | 
						|
	def report_closure(dd): # Print reports on dd and on subdirs
 | 
						|
		# If phase 4 hasn't been done, no subdir reports are printed
 | 
						|
		dd.report()
 | 
						|
		try:
 | 
						|
			x = dd.subdirs
 | 
						|
		except AttributeError:
 | 
						|
			return # No subdirectories computed
 | 
						|
		for x in dd.subdirs.keys():
 | 
						|
			print
 | 
						|
			dd.subdirs[x].report_closure()
 | 
						|
	#
 | 
						|
	def report_phase4_closure(dd): # Report and do phase 4 recursively
 | 
						|
		dd.report()
 | 
						|
		dd.phase4()
 | 
						|
		for x in dd.subdirs.keys():
 | 
						|
			print
 | 
						|
			dd.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(path.join(a, x), 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 posix.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()
 |