#!/usr/bin/env python

"""
zcfv - Zlib CRC File Validator (1999-11-18)
------------------------------------------
 Most of the time you will need no command line arguments. By default
 if zfv-crc.txt exists in the current directory, files will be verified;
 if it doesn't exist, then a zfv-crc.txt file will be created.

 -t     - force TEST files in current directory against stored CRC values
 -c     - force CREATION (or re-creation) of CRC text file in current directory
 -s     - case SENSITIVITY in filenames, default is non-sensitive
 -r     - RECURSE through subdirectories (writes/reads file in each dir)
 -x     - EXPUNGE (delete) the zfv-crc.txt file from directory(s)
 -d     - DELETE any files with mismatched CRC values (use with care!)
 -1     - do not write CRC creation lines to screen (default: screen & file)
 -2     - do not write CRC creation lines to file (default: screen & file)
 -f filename    - alternate FILENAME to use other than zfv-crc.txt
 -l logfile     - LOG errors to this file (warning: no file sharing support)
 -b buffersize  - set the file BUFFER size in bytes (default: 131072)

CRC calculations here are ZIP/Zlib compatible (not SFV compatible).
T. Middleton - http://www.vex.net/~x/
"""

# note: you can change the buffer size (default 256k) below at bufsize=

crcfilename = "zfv-crc.txt"

recurse=0           # actually technically it's not recursion
mode=0              # 0 = auto; 2 = test; 1 = create;
printmode=3         # 3 = stdout + file; 1 = file only; 2 = stdout only
caseSensitive=0     # filename matches
deathmode = 0       # delete files with wrong CRC
buffersize = 131072 # file buffer size, default 131072 bytes (128k)


import os,sys,re
import zlib,glob,time,getopt,string

reFv = re.compile(r"^([^#\n]\S+)\s+(\S+\s+\S+)\s+(\d+)\s\s(.*)\s*$",re.M)
Gerrors = 0     # global errors (collected when recursing)
Gbytes = 0
Gfiles = 0
logfile = None
dirs = [None]

def crcfile(fn,bufsize=131072):
    """Calculate CRC for the given filename, return longint
    """
    crc = None
    try:
        f = open(fn,"rb")
        while 1:
            s = f.read(bufsize)
            if crc==None:
                crc = zlib.crc32(s)
            else:
                crc = zlib.crc32(s,crc)
            if len(s)crcfilename:
            stat = os.stat(fn)
            crc = crcfile(fn,buffersize)
            crcprint(fout,"%08X  %s%11d  %s" % (
                crc,
                time.strftime("%Y-%m-%y %H:%M:%S",time.localtime(stat[8])),
                stat[6],
                fn )
                )
            fcount = fcount + 1
            bcount = bcount + stat[6]
    return (fcount,bcount)

def crcprint(fout,s):
    """Print to file and/or stdout
    """
    if (printmode & 1 and fout):
        fout.write(s+"\n")
    if printmode & 2:
        print s

def errprint(logfile,s):
    """Append error to log file, and print to screen
    """
    if logfile:
        try:
            f = open(logfile,'a')
            #f.seek(0,2)
            f.write(s+"\n")
            f.close()
        except IOError,msg:
            pass
    print s

def calcfiles():
    """Calculate all the CRCs
    """
    global Gbytes,Gfiles
    try:
        fout = open(crcfilename,'w')

        tnow = time.time()
        crcprint(fout,"# zcfv CRC data - %s\n" %
            time.strftime("%Y-%m-%y %H:%M:%S (%A)",time.localtime(time.time())))
        fcount,bcount = dodir(fout)
        crcprint(fout,"\n# %s files (%d bytes) processed in %.2f seconds." % (
                fcount, bcount, time.time() - tnow) )
        if (printmode & 1 and fout):
            fout.write("# Get zcfv from http://www.geocities.com/SoHo/1826/zcfv.html\n")
        Gfiles = Gfiles + fcount
        Gbytes = Gbytes + bcount
        fout.close()
    except IOError,msg:
        print "ABORT: Error writing to %s: %s" % (
                os.path.abspath(crcfilename),msg )
        sys.exit()

def compfiles():
    """Load and parse and compare CRC file to actual files in current dir
    """
    global Gfiles,Gbytes
    try:
        f = open(crcfilename,"r")
        fv = f.read()
        f.close()
    except IOError,msg:
        print "ABORT: Error reading to %s: %s" % (crcfilename,msg)
        sys.exit()
    vals = reFv.findall(fv)
    dict = {}
    for v in vals:
        if caseSensitive:
            dict[v[-1]] = v[:-1]
        else:
            dict[string.upper(v[-1])] = v[:-1]

    errors = 0
    warnings = 0

    files = glob.glob("*")
    for fn in files:
        if os.path.isdir(fn):
            if recurse:
                dirs.append(os.path.abspath(fn))
        elif fn<>crcfilename:
            if caseSensitive:
                gotfile=dict.get(fn,None)
            else:
                gotfile=dict.get(string.upper(fn),None)
            if gotfile<>None:
                crc = crcfile(fn,buffersize)
                stat = os.stat(fn)
                Gbytes = Gbytes + stat[6]
                if gotfile[0] <> ("%08X" % crc):
                    errprint(logfile,"! Bad CRC: %08X should be %s for %s" % (
                            crc,gotfile[0],os.path.abspath(fn),) )
                    errors = errors + 1
                    if deathmode:
                        try:
                            os.unlink(fn)
                            print "# Deleted %s" % os.path.abspath(fn)
                        except IOError,msg:
                            errprint(logfile,"! Couldn't delete %s" % (
                                    os.path.abspath(fn),) )
            else:
                print "* %s is in directory, but not in %s." % (fn,crcfilename)
                warnings = warnings + 1
    if warnings==0 and errors==0:
        print "Out of %s files all appear correct." % len(dict.keys())
    if warnings>0:
        print "* %s files are listed in %s, but %s in the directory." % (
                len(dict.keys()),crcfilename,len(files) )
    if errors>0:
        print "! Out of %s files %s have incorrect CRC values." % (
                len(dict.keys()),errors )
    Gfiles = Gfiles + len(dict.keys())
    return errors

def getdirs():
    """return list of subdirectories of current directory
    """
    fns = glob.glob("*")
    dirs = []
    for fn in fns:
        if os.path.isdir(fn):
            dirs.append(os.path.abspath(fn))
    return dirs

def help():
    print globals()["__doc__"]
    sys.exit()

opts,args = getopt.getopt(sys.argv[1:],"12CcDdHhRrSsTtXxB:b:F:f:L:l:")

for opt,arg in opts:
    if  opt=="-h" or opt=="-H" or opt=="-?":
        help()
    elif opt=="-f" or opt=="-F":
        crcfilename = arg
    elif opt=="-l" or opt=="-L":
        logfile = arg
    elif opt=="-b" or opt=="-B":
        try:
            buffersize = string.atoi(arg)
        except:
            print "! Invalid buffersize (%s), using default (%s)" % (
                    arg,buffersize )
    elif opt=="-c" or opt=="-C":
        mode = 1
    elif opt=="-t" or opt=="-T":
        mode = 2
    elif opt=="-x" or opt=="-X":
        mode = 3
    elif opt=="-s" or opt=="-S":
        caseSensitive = 1
    elif opt=="-r" or opt=="-R":
        recurse = 1
    elif opt=="-d" or opt=="-D":
        deathmode = 1
    elif opt=="-1":
        printmode = 1
    elif opt=="-2":
        printmode = 2

for arg in args:
    if arg=="c" or arg=="C":
        mode = 1
    elif arg=="t" or arg=="T":
        mode = 2
    elif arg=="x" or arg=="X":
        mode = 3
    elif arg=="/?" or arg=="/h" or arg=="/H":
        help()
    else:
        crcfilename = arg

dcount = 0
dnow = time.time()
odir = os.getcwd()
while dirs:
    if dirs[0]<>None:
        os.chdir(os.path.abspath(dirs[0]))
        print "# Entering %s..." % os.path.abspath(dirs[0])

    if mode == 0:
        if os.path.exists(crcfilename):
            mode = 2
        else:
            mode = 1

    if mode == 3:
        if os.path.exists(crcfilename):
            try:
                os.unlink(crcfilename)
                print "# Delated %s" % os.path.abspath(crcfilename)
            except IOError,msg:
                print "! Can't delete %s" % os.path.abspath(crcfilename)
        if recurse:
            dirs[len(dirs):len(dirs)] = getdirs()
    elif mode == 1:
        calcfiles()
    else:
        Gerrors = Gerrors + compfiles()
    dcount = dcount + 1
    del dirs[0]

if dcount>1:
    print "# Scanned %s directories (%s files, %d bytes) in %.2f seconds" % (
            dcount,Gfiles,Gbytes,time.time() - dnow )
    if Gerrors > 0:
        print "# There were %s mismatching CRC values found in total!" % Gerrors

os.chdir(odir)


    Source: geocities.com/soho/1826

               ( geocities.com/soho)