home *** CD-ROM | disk | FTP | other *** search
/ PC World 2005 June / PCWorld_2005-06_cd.bin / software / vyzkuste / firewally / firewally.exe / framework-2.3.exe / dircmp.py < prev    next >
Text File  |  2003-12-30  |  6KB  |  203 lines

  1. """A class to build directory diff tools on."""
  2.  
  3. import os
  4.  
  5. import dircache
  6. import cmpcache
  7. import statcache
  8. from stat import *
  9.  
  10. class dircmp:
  11.     """Directory comparison class."""
  12.  
  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
  20.  
  21.         return self
  22.  
  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()
  32.  
  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)
  42.  
  43.         self.b_only = []
  44.         for x in self.b_list:
  45.             if x not in self.common:
  46.                 self.b_only.append(x)
  47.  
  48.     def phase2(self):
  49.         """Distinguish files, directories, funnies."""
  50.         self.common_dirs = []
  51.         self.common_files = []
  52.         self.common_funny = []
  53.  
  54.         for x in self.common:
  55.             a_path = os.path.join(self.a, x)
  56.             b_path = os.path.join(self.b, x)
  57.  
  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
  69.  
  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)
  83.  
  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
  88.  
  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()
  102.  
  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()
  108.  
  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
  128.  
  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()
  140.  
  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()
  148.  
  149.  
  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.)"""
  156.  
  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
  161.  
  162.  
  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.)"""
  169.  
  170.     try:
  171.         if cmpcache.cmp(a, b): return 0
  172.         return 1
  173.     except os.error:
  174.         return 2
  175.  
  176.  
  177. def filter(list, skip):
  178.     """Return a copy with items that occur in skip removed."""
  179.  
  180.     result = []
  181.     for item in list:
  182.         if item not in skip: result.append(item)
  183.     return result
  184.  
  185.  
  186. def demo():
  187.     """Demonstration and testing."""
  188.  
  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()
  200.  
  201. if __name__ == "__main__":
  202.     demo()
  203.