Its right now.
[python.git] / Lib / lib-old / dircmp.py
blob1e7bf2ab202e4425338c996b2adcbb76b41842c8
1 """A class to build directory diff tools on."""
3 import os
5 import dircache
6 import cmpcache
7 import statcache
8 from stat import *
10 class dircmp:
11 """Directory comparison class."""
13 def new(self, a, b):
14 """Initialize."""
15 self.a = a
16 self.b = b
17 # Properties that caller may change before calling self.run():
18 self.hide = [os.curdir, os.pardir] # Names never to be shown
19 self.ignore = ['RCS', 'tags'] # Names ignored in comparison
21 return self
23 def run(self):
24 """Compare everything except common subdirectories."""
25 self.a_list = filter(dircache.listdir(self.a), self.hide)
26 self.b_list = filter(dircache.listdir(self.b), self.hide)
27 self.a_list.sort()
28 self.b_list.sort()
29 self.phase1()
30 self.phase2()
31 self.phase3()
33 def phase1(self):
34 """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)
40 else:
41 self.a_only.append(x)
43 self.b_only = []
44 for x in self.b_list:
45 if x not in self.common:
46 self.b_only.append(x)
48 def phase2(self):
49 """Distinguish files, directories, funnies."""
50 self.common_dirs = []
51 self.common_files = []
52 self.common_funny = []
54 for x in self.common:
55 a_path = os.path.join(self.a, x)
56 b_path = os.path.join(self.b, x)
58 ok = 1
59 try:
60 a_stat = statcache.stat(a_path)
61 except os.error, why:
62 # print 'Can\'t stat', a_path, ':', why[1]
63 ok = 0
64 try:
65 b_stat = statcache.stat(b_path)
66 except os.error, why:
67 # print 'Can\'t stat', b_path, ':', why[1]
68 ok = 0
70 if ok:
71 a_type = S_IFMT(a_stat[ST_MODE])
72 b_type = S_IFMT(b_stat[ST_MODE])
73 if a_type != b_type:
74 self.common_funny.append(x)
75 elif S_ISDIR(a_type):
76 self.common_dirs.append(x)
77 elif S_ISREG(a_type):
78 self.common_files.append(x)
79 else:
80 self.common_funny.append(x)
81 else:
82 self.common_funny.append(x)
84 def phase3(self):
85 """Find out differences between common files."""
86 xx = cmpfiles(self.a, self.b, self.common_files)
87 self.same_files, self.diff_files, self.funny_files = xx
89 def phase4(self):
90 """Find out differences between common subdirectories.
91 A new dircmp object is created for each common subdirectory,
92 these are stored in a dictionary indexed by filename.
93 The hide and ignore properties are inherited from the parent."""
94 self.subdirs = {}
95 for x in self.common_dirs:
96 a_x = os.path.join(self.a, x)
97 b_x = os.path.join(self.b, x)
98 self.subdirs[x] = newdd = dircmp().new(a_x, b_x)
99 newdd.hide = self.hide
100 newdd.ignore = self.ignore
101 newdd.run()
103 def phase4_closure(self):
104 """Recursively call phase4() on subdirectories."""
105 self.phase4()
106 for x in self.subdirs.keys():
107 self.subdirs[x].phase4_closure()
109 def report(self):
110 """Print a report on the differences between a and b."""
111 # Assume that phases 1 to 3 have been executed
112 # Output format is purposely lousy
113 print 'diff', self.a, self.b
114 if self.a_only:
115 print 'Only in', self.a, ':', self.a_only
116 if self.b_only:
117 print 'Only in', self.b, ':', self.b_only
118 if self.same_files:
119 print 'Identical files :', self.same_files
120 if self.diff_files:
121 print 'Differing files :', self.diff_files
122 if self.funny_files:
123 print 'Trouble with common files :', self.funny_files
124 if self.common_dirs:
125 print 'Common subdirectories :', self.common_dirs
126 if self.common_funny:
127 print 'Common funny cases :', self.common_funny
129 def report_closure(self):
130 """Print reports on self and on subdirs.
131 If phase 4 hasn't been done, no subdir reports are printed."""
132 self.report()
133 try:
134 x = self.subdirs
135 except AttributeError:
136 return # No subdirectories computed
137 for x in self.subdirs.keys():
138 print
139 self.subdirs[x].report_closure()
141 def report_phase4_closure(self):
142 """Report and do phase 4 recursively."""
143 self.report()
144 self.phase4()
145 for x in self.subdirs.keys():
146 print
147 self.subdirs[x].report_phase4_closure()
150 def cmpfiles(a, b, common):
151 """Compare common files in two directories.
152 Return:
153 - files that compare equal
154 - files that compare different
155 - funny cases (can't stat etc.)"""
157 res = ([], [], [])
158 for x in common:
159 res[cmp(os.path.join(a, x), os.path.join(b, x))].append(x)
160 return res
163 def cmp(a, b):
164 """Compare two files.
165 Return:
166 0 for equal
167 1 for different
168 2 for funny cases (can't stat, etc.)"""
170 try:
171 if cmpcache.cmp(a, b): return 0
172 return 1
173 except os.error:
174 return 2
177 def filter(list, skip):
178 """Return a copy with items that occur in skip removed."""
180 result = []
181 for item in list:
182 if item not in skip: result.append(item)
183 return result
186 def demo():
187 """Demonstration and testing."""
189 import sys
190 import getopt
191 options, args = getopt.getopt(sys.argv[1:], 'r')
192 if len(args) != 2:
193 raise getopt.error, 'need exactly two args'
194 dd = dircmp().new(args[0], args[1])
195 dd.run()
196 if ('-r', '') in options:
197 dd.report_phase4_closure()
198 else:
199 dd.report()
201 if __name__ == "__main__":
202 demo()