blob: 1227aa753c3a4683f756ab178da3240c56098af5 [file] [log] [blame]
Guido van Rossumc6360141990-10-13 19:23:40 +00001# Module 'dirmp'
2#
3# Defines a class to build directory diff tools on.
4
Guido van Rossumc96207a1992-03-31 18:55:40 +00005import os
Guido van Rossumc6360141990-10-13 19:23:40 +00006
7import dircache
8import cmpcache
9import statcache
Guido van Rossum40d93041990-10-21 16:17:34 +000010from stat import *
Guido van Rossumc6360141990-10-13 19:23:40 +000011
12# Directory comparison class.
13#
Guido van Rossumbb3753d1991-12-26 13:03:23 +000014class dircmp:
Guido van Rossumc6360141990-10-13 19:23:40 +000015 #
Guido van Rossum89a78691992-12-14 12:57:56 +000016 def new(self, a, b): # Initialize
17 self.a = a
18 self.b = b
19 # Properties that caller may change before calling self.run():
20 self.hide = [os.curdir, os.pardir] # Names never to be shown
21 self.ignore = ['RCS', 'tags'] # Names ignored in comparison
Guido van Rossumc6360141990-10-13 19:23:40 +000022 #
Guido van Rossum89a78691992-12-14 12:57:56 +000023 return self
Guido van Rossumc6360141990-10-13 19:23:40 +000024 #
Guido van Rossum89a78691992-12-14 12:57:56 +000025 def run(self): # Compare everything except common subdirectories
26 self.a_list = filter(dircache.listdir(self.a), self.hide)
27 self.b_list = filter(dircache.listdir(self.b), self.hide)
28 self.a_list.sort()
29 self.b_list.sort()
30 self.phase1()
31 self.phase2()
32 self.phase3()
Guido van Rossumc6360141990-10-13 19:23:40 +000033 #
Guido van Rossum89a78691992-12-14 12:57:56 +000034 def phase1(self): # Compute common names
35 self.a_only = []
36 self.common = []
37 for x in self.a_list:
38 if x in self.b_list:
39 self.common.append(x)
Guido van Rossumc6360141990-10-13 19:23:40 +000040 else:
Guido van Rossum89a78691992-12-14 12:57:56 +000041 self.a_only.append(x)
Guido van Rossumc6360141990-10-13 19:23:40 +000042 #
Guido van Rossum89a78691992-12-14 12:57:56 +000043 self.b_only = []
44 for x in self.b_list:
45 if x not in self.common:
46 self.b_only.append(x)
Guido van Rossumc6360141990-10-13 19:23:40 +000047 #
Guido van Rossum89a78691992-12-14 12:57:56 +000048 def phase2(self): # Distinguish files, directories, funnies
49 self.common_dirs = []
50 self.common_files = []
51 self.common_funny = []
Guido van Rossumc6360141990-10-13 19:23:40 +000052 #
Guido van Rossum89a78691992-12-14 12:57:56 +000053 for x in self.common:
54 a_path = os.path.join(self.a, x)
55 b_path = os.path.join(self.b, x)
Guido van Rossumc6360141990-10-13 19:23:40 +000056 #
57 ok = 1
58 try:
59 a_stat = statcache.stat(a_path)
Guido van Rossumc96207a1992-03-31 18:55:40 +000060 except os.error, why:
Guido van Rossumc6360141990-10-13 19:23:40 +000061 # print 'Can\'t stat', a_path, ':', why[1]
62 ok = 0
63 try:
64 b_stat = statcache.stat(b_path)
Guido van Rossumc96207a1992-03-31 18:55:40 +000065 except os.error, why:
Guido van Rossumc6360141990-10-13 19:23:40 +000066 # print 'Can\'t stat', b_path, ':', why[1]
67 ok = 0
68 #
69 if ok:
Guido van Rossum40d93041990-10-21 16:17:34 +000070 a_type = S_IFMT(a_stat[ST_MODE])
71 b_type = S_IFMT(b_stat[ST_MODE])
Guido van Rossumc6360141990-10-13 19:23:40 +000072 if a_type <> b_type:
Guido van Rossum89a78691992-12-14 12:57:56 +000073 self.common_funny.append(x)
Guido van Rossum40d93041990-10-21 16:17:34 +000074 elif S_ISDIR(a_type):
Guido van Rossum89a78691992-12-14 12:57:56 +000075 self.common_dirs.append(x)
Guido van Rossum40d93041990-10-21 16:17:34 +000076 elif S_ISREG(a_type):
Guido van Rossum89a78691992-12-14 12:57:56 +000077 self.common_files.append(x)
Guido van Rossumc6360141990-10-13 19:23:40 +000078 else:
Guido van Rossum89a78691992-12-14 12:57:56 +000079 self.common_funny.append(x)
Guido van Rossumc6360141990-10-13 19:23:40 +000080 else:
Guido van Rossum89a78691992-12-14 12:57:56 +000081 self.common_funny.append(x)
Guido van Rossumc6360141990-10-13 19:23:40 +000082 #
Guido van Rossum89a78691992-12-14 12:57:56 +000083 def phase3(self): # Find out differences between common files
84 xx = cmpfiles(self.a, self.b, self.common_files)
85 self.same_files, self.diff_files, self.funny_files = xx
Guido van Rossumc6360141990-10-13 19:23:40 +000086 #
Guido van Rossum89a78691992-12-14 12:57:56 +000087 def phase4(self): # Find out differences between common subdirectories
Guido van Rossumc6360141990-10-13 19:23:40 +000088 # A new dircmp object is created for each common subdirectory,
89 # these are stored in a dictionary indexed by filename.
90 # The hide and ignore properties are inherited from the parent
Guido van Rossum89a78691992-12-14 12:57:56 +000091 self.subdirs = {}
92 for x in self.common_dirs:
93 a_x = os.path.join(self.a, x)
94 b_x = os.path.join(self.b, x)
95 self.subdirs[x] = newdd = dircmp().new(a_x, b_x)
96 newdd.hide = self.hide
97 newdd.ignore = self.ignore
Guido van Rossumc6360141990-10-13 19:23:40 +000098 newdd.run()
99 #
Guido van Rossum89a78691992-12-14 12:57:56 +0000100 def phase4_closure(self): # Recursively call phase4() on subdirectories
101 self.phase4()
102 for x in self.subdirs.keys():
103 self.subdirs[x].phase4_closure()
Guido van Rossumc6360141990-10-13 19:23:40 +0000104 #
Guido van Rossum89a78691992-12-14 12:57:56 +0000105 def report(self): # Print a report on the differences between a and b
Guido van Rossumc6360141990-10-13 19:23:40 +0000106 # Assume that phases 1 to 3 have been executed
107 # Output format is purposely lousy
Guido van Rossum89a78691992-12-14 12:57:56 +0000108 print 'diff', self.a, self.b
109 if self.a_only:
110 print 'Only in', self.a, ':', self.a_only
111 if self.b_only:
112 print 'Only in', self.b, ':', self.b_only
113 if self.same_files:
114 print 'Identical files :', self.same_files
115 if self.diff_files:
116 print 'Differing files :', self.diff_files
117 if self.funny_files:
118 print 'Trouble with common files :', self.funny_files
119 if self.common_dirs:
120 print 'Common subdirectories :', self.common_dirs
121 if self.common_funny:
122 print 'Common funny cases :', self.common_funny
Guido van Rossumc6360141990-10-13 19:23:40 +0000123 #
Guido van Rossum89a78691992-12-14 12:57:56 +0000124 def report_closure(self): # Print reports on self and on subdirs
Guido van Rossumc6360141990-10-13 19:23:40 +0000125 # If phase 4 hasn't been done, no subdir reports are printed
Guido van Rossum89a78691992-12-14 12:57:56 +0000126 self.report()
Guido van Rossumc6360141990-10-13 19:23:40 +0000127 try:
Guido van Rossum89a78691992-12-14 12:57:56 +0000128 x = self.subdirs
Guido van Rossumbb3753d1991-12-26 13:03:23 +0000129 except AttributeError:
Guido van Rossumc6360141990-10-13 19:23:40 +0000130 return # No subdirectories computed
Guido van Rossum89a78691992-12-14 12:57:56 +0000131 for x in self.subdirs.keys():
Guido van Rossumc6360141990-10-13 19:23:40 +0000132 print
Guido van Rossum89a78691992-12-14 12:57:56 +0000133 self.subdirs[x].report_closure()
Guido van Rossumc6360141990-10-13 19:23:40 +0000134 #
Guido van Rossum89a78691992-12-14 12:57:56 +0000135 def report_phase4_closure(self): # Report and do phase 4 recursively
136 self.report()
137 self.phase4()
138 for x in self.subdirs.keys():
Guido van Rossumc6360141990-10-13 19:23:40 +0000139 print
Guido van Rossum89a78691992-12-14 12:57:56 +0000140 self.subdirs[x].report_phase4_closure()
Guido van Rossumc6360141990-10-13 19:23:40 +0000141
142
143# Compare common files in two directories.
144# Return:
145# - files that compare equal
146# - files that compare different
147# - funny cases (can't stat etc.)
148#
149def cmpfiles(a, b, common):
150 res = ([], [], [])
151 for x in common:
Guido van Rossumc96207a1992-03-31 18:55:40 +0000152 res[cmp(os.path.join(a, x), os.path.join(b, x))].append(x)
Guido van Rossumc6360141990-10-13 19:23:40 +0000153 return res
154
155
156# Compare two files.
157# Return:
158# 0 for equal
159# 1 for different
160# 2 for funny cases (can't stat, etc.)
161#
162def cmp(a, b):
163 try:
164 if cmpcache.cmp(a, b): return 0
165 return 1
Guido van Rossumc96207a1992-03-31 18:55:40 +0000166 except os.error:
Guido van Rossumc6360141990-10-13 19:23:40 +0000167 return 2
168
169
170# Remove a list item.
171# NB: This modifies the list argument.
172#
173def remove(list, item):
174 for i in range(len(list)):
Guido van Rossumbdfcfcc1992-01-01 19:35:13 +0000175 if list[i] == item:
Guido van Rossumc6360141990-10-13 19:23:40 +0000176 del list[i]
177 break
178
179
180# Return a copy with items that occur in skip removed.
181#
182def filter(list, skip):
183 result = []
184 for item in list:
185 if item not in skip: result.append(item)
186 return result
187
188
189# Demonstration and testing.
190#
191def demo():
192 import sys
193 import getopt
194 options, args = getopt.getopt(sys.argv[1:], 'r')
195 if len(args) <> 2: raise getopt.error, 'need exactly two args'
196 dd = dircmp().new(args[0], args[1])
197 dd.run()
198 if ('-r', '') in options:
199 dd.report_phase4_closure()
200 else:
201 dd.report()
202
203# demo()