#!/usr/bin/python
'''compares two sandboxes and copy new and changed files to new directory'''

import os, sys, re, string, shutil, fnmatch
#, shutil
#from stat import *

diff_dir = '_diff4cvs'
diff_command = "diff --brief -r"
include_file_patterns = ['*']
exclude_file_patterns = ['*~']
exclude_dir_patterns = ['CVS', '_*']
o_verbose = False
my_log = ''
#diff_command = "diff --brief -r -x CVS -x _build -x _compiled -x %s" % diff_dir

def log(s):
    global my_log
    print s
    my_log += s + '\n'

def wild_match(name, patterns):
    #print file_name, filename, patterns
    for p in patterns:
        if fnmatch.fnmatchcase(name, p):
            return True
    return False

def dir_accepted(path):
    if path == diff_dir: return True
    s = string.split(path, '/')
    for f in s:
        if wild_match(f, exclude_dir_patterns):
            return False
    return True
    
def file_accepted(filename):
    #filename = os.path.split(filename)[1]
    if filename == '':
        return True # directory is always accepted
    if wild_match(filename, exclude_file_patterns):
        return False
    if wild_match(filename, include_file_patterns):
        return True
    return False
    
def mkdirs(path, mode=0777):
    #print "creating: '%s'" % path,
    if os.access(path, os.W_OK): return True
    try:
        os.makedirs(path, mode)
        #print 'OK'
        return True
    except os.error, why:
        pass
        #print
        print 'ERROR', why
    return False

class cpError(Exception):
     def __init__(self, msg):
         self.msg = msg
     def __str__(self):
         return self.msg

def cp(src, dst, symlinks=0):
    #print 'cp:', src, '-->', dst
    if not mkdirs(dst): return
    if os.path.isdir(src):
        if not os.path.isdir(dst):
            raise cpError("Cann't copy directory '%s' to the file '%s'" % (src, dst))
        names = os.listdir(src)
        for name in names:
            srcname = os.path.join(src, name)
            dstname = os.path.join(dst, name)
            try:
                if symlinks and os.path.islink(srcname):
                    linkto = os.readlink(srcname)
                    os.symlink(linkto, dstname)
                elif os.path.isdir(srcname):
                    if not dir_accepted(srcname):
                        continue
                    cp(srcname, dstname, symlinks)
                else:
                    if not file_accepted(srcname):
                        continue
                    shutil.copy2(srcname, dstname)
            except (IOError, os.error), why:
                print "Can't copy %s to %s: %s" % (`srcname`, `dstname`, str(why))
    else:
        if o_verbose: print 'copy file:', src, '-->', dst
        shutil.copy2(src, dst)        
        
if __name__ == '__main__':
    help_msg = '''SYNOPSIS: diff4cvs old_cvs_dir [fresh_cvs_dir]
          prepare filelist to synchronize old_cvs_dir with fresh_cvs_dir
          result is stored in %s
          the default fresh_cvs_dir is current dir
OPTIONS:
          -v more verbose output 
          -X "wildcard1,wildcard2, ..." 
             does not copy matching directories, 
             default is -X "CVS,_*"
          -x "wildcard1,wildcard2, ..." 
             does not copy matching files, 
             stronger than -i option
             default is -x "*~"
          -i "wildcard1,wildcard2, ..." 
             does copy only matching files
             default is -i "*"'''
    argv = sys.argv[1:]
    o_help = False
    if len(argv) == 0:
        o_help = True
    args = []
    i = 0
    while i<len(argv):
        a = argv[i]
        if a == '-h':
            o_help = True
            break
        elif a == '-v':
            o_verbose = True
        elif a == '-X':
            exclude_dir_patterns = string.split(argv[i+1], ',')
            i = i + 1
        elif a == '-x':
            exclude_file_patterns = string.split(argv[i+1], ',')
            i = i + 1
        elif a == '-i':
            include_file_patterns = string.split(argv[i+1], ',')
            i = i + 1
	else:
    	    args.append(a)
        i = i + 1
        
    if o_help:
        print 
        print help_msg % diff_dir
        print 
        sys.exit(0)

    old_dir = args[0];
    if len(args) > 1:
        fresh_dir = args[1]
    else:
        fresh_dir = '.'
    
    shutil.rmtree(diff_dir, True)
    
    ss = ''
    for s in exclude_dir_patterns:
        ss += ' -x "' + s + '"'
    diff_command += ss + ' ' + old_dir + ' ' + fresh_dir
    print diff_command
    out = os.popen(diff_command)
    files = []
    while True:
        line = out.readline()
        if len(line) == 0:
            break
            
        # find new files
        wanted = 'Only in ' + fresh_dir
        ret = string.find(line, wanted)
        if ret == 0:
            #print line,
            s = line[len(wanted):] 
            if s[0] == '/': s = s[1:] # remove also leading '/' if exists
            (p, f) = string.split(s, ':')
            p = string.strip(p)
            f = string.strip(f)
            # check if different is file or directory
            s = os.path.join(fresh_dir, p, f)
            if os.path.isdir(s):
                p = os.path.join(p, f)
                f = ''
            files.append(['NEW', p, f])
            #print s
            #print p, f
            continue
            
        #find different files
        s = string.split(line)
        if len(s) != 5:
            continue
        if s[0] == 'Files' and s[2] == 'and' and s[4] == 'differ':
            #print line,
            f = s[3]
            f = f[len(fresh_dir)+1:]
            (p, f) = os.path.split(f)
            files.append(['DIF', p, f])
            #print f
    out.close()
    
    # filter different or new files
    oldfiles = files
    files = []
    for s in oldfiles:
        (p, f) = (s[1], s[2])
        if not dir_accepted(p): continue
        if not file_accepted(f): continue
        files.append(s)
    
    print
    log('EXCLUDED DIRS : ' + `exclude_dir_patterns`)
    log('EXCLUDED FILES: ' + `exclude_file_patterns`)
    log('INCLUDED FILES: ' + `include_file_patterns`)
    print
    log('COPIED FILES:')
    for s in files:
        log(`s`)

    for s in files:
        (p, f) = (s[1], s[2])
        dst = os.path.join(diff_dir, p)
        src = os.path.join(fresh_dir, p, f)
        #print 'copy:', src, '->', dst
        #print
        cp(src, dst)
    
    log_file = os.path.join(diff_dir, '_diff4cvs.log')
    f = open(log_file, 'wb')
    f.write(my_log)
    f.close()
    