cfv-1.18.3/0002755000175300017530000000000011212670127011435 5ustar donutdonutcfv-1.18.3/cfv0000755000175300017530000023476311212667705012166 0ustar donutdonut#! /usr/bin/env python # cfv - Command-line File Verify # Copyright (C) 2000-2009 Matthew Mueller # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import getopt, re, os, sys, errno, time, copy, struct, codecs from stat import * cftypes={} _cf_fn_exts, _cf_fn_matches, _cf_fn_searches = [], [], [] _cf_matchers = [] LISTOK=512 LISTBAD=1024 LISTNOTFOUND=2048 LISTUNVERIFIED=4096 LISTARGS={'ok':LISTOK, 'bad':LISTBAD, 'notfound':LISTNOTFOUND, 'unverified':LISTUNVERIFIED} class Data: def __init__(self, **kw): self.__dict__.update(kw) def chomp(line): if line[-2:] == '\r\n': return line[:-2] elif line[-1:] in '\r\n': return line[:-1] return line def chompnulls(line): p = line.find('\0') if p < 0: return line else: return line[:p] def lchoplen(line, max): if len(line)>max: return '...'+line[-(max-3):] return line try: ''.lstrip('a') #str.lstrip(arg) only in python>=2.2 def lstrip(s,c): return s.lstrip(c) except TypeError: def lstrip(s, c): for i in range(0,len(s)): if s[i]!=c: break return s[i:] try: sorted #sorted(seq) only in python>=2.4 except NameError: def sorted(seq): l = list(seq) l.sort() return l _realpath = getattr(os.path, 'realpath', os.path.abspath) #realpath is only in Python>=2.2 _path_key_cache = {} def get_path_key(path): dk = _path_key_cache.get(path) if dk is not None: return dk st = os.stat(path or os.curdir) if st[ST_INO]: dk = (st[ST_DEV], st[ST_INO]) else: dk = _realpath(os.path.join(curdir, path)) _path_key_cache[path] = dk return dk curdir=os.getcwd() reldir=[''] prevdir=[] def chdir(d): global curdir,_path_key_cache os.chdir(d) prevdir.append((curdir,_path_key_cache)) reldir.append(os.path.join(reldir[-1], d)) curdir=os.getcwd() _path_key_cache = {} def cdup(): global curdir,_path_key_cache reldir.pop() curdir,_path_key_cache=prevdir.pop() os.chdir(curdir) def getscrwidth(): w = -1 try: from fcntl import ioctl try: from termios import TIOCGWINSZ except ImportError: from TERMIOS import TIOCGWINSZ tty = sys.stdin.isatty() and sys.stdin or sys.stdout.isatty() and sys.stdout or sys.stderr.isatty() and sys.stderr or None if tty: h,w = struct.unpack('h h', ioctl(tty.fileno(), TIOCGWINSZ, '\0'*struct.calcsize('h h'))) except ImportError: pass if w>0: return w c = os.environ.get('COLUMNS',80) try: return int(c) except ValueError: return 80 scrwidth = getscrwidth() stdout = sys.stdout stderr = sys.stderr _stdout_special = 0 stdinfo = sys.stdout stdprogress = None def set_stdout_special(): """If stdout is being used for special purposes, redirect informational messages to stderr.""" global stdinfo, _stdout_special _stdout_special = 1 stdinfo = stderr class CodecWriter: """Similar to codecs.StreamWriter, but str objects are passed through directly to the output stream. This is necessary as some codecs barf on trying to encode even ascii strings. Also, filenames read from disk will not be unicode, but rather strings encoded in the filesystemencoding, which will make any StreamWriter barf. But printing them directly should (hopefully) work fine. """ #Another approach could be to convert str objects to unicode and then pass them to encode, but that would be extra work. def __init__(self, encoding, stream, errors='strict'): self.stream = stream self.errors = errors self.encode = codecs.lookup(encoding)[0] #codecs.getencoder(encoding) #codecs.getencoder is only in python2.2+, but lookup(...)[0] is equivalent def write(self, object): if type(object)==type(''): self.stream.write(object) else: data, consumed = self.encode(object, self.errors) self.stream.write(data) def writelines(self, list): self.write(''.join(list)) def __getattr__(self, name, getattr=getattr): """ Inherit all other methods from the underlying stream. """ return getattr(self.stream, name) codec_error_handler = 'backslashreplace' try: codecs.lookup_error(codec_error_handler) #backslashreplace is only in python2.3+ except: codec_error_handler = 'replace' def setup_output(): global stdinfo,stdprogress,stdout,stderr stdout = CodecWriter(getattr(sys.stdout,'encoding',None) or 'ascii', sys.stdout, errors=codec_error_handler) stderr = CodecWriter(getattr(sys.stderr,'encoding',None) or getattr(sys.stdout,'encoding',None) or 'ascii', sys.stderr, errors=codec_error_handler) stdinfo = _stdout_special and stderr or stdout # if one of stdinfo (usually stdout) or stderr is a tty, use it. Otherwise use stdinfo. stdprogress = stdinfo.isatty() and stdinfo or stderr.isatty() and stderr or stdinfo doprogress = not config.verbose==-2 and ( config.progress=='y' or ( config.progress=='a' and stdprogress.isatty() ) ) if not doprogress: stdprogress = None def pverbose(s,nl='\n'): if config.verbose>0: stdinfo.write(s+nl) def pinfo(s,nl='\n'): if config.verbose>=0 or config.verbose==-3: stdinfo.write(s+nl) def perror(s,nl='\n'): if config.verbose>=-1: stdout.flush() # avoid inconsistent screen state if stdout has unflushed data stderr.write(s+nl) def plistf(filename): stdout.write(perhaps_showpath(filename)+config.listsep) class INF: """object that is always larger than what it is compared to""" def __cmp__(self, other): return 1 def __mul__(self, other): return self def __div__(self, other): return self def __rdiv__(self, other): return 0 INF=INF() class ProgressMeter: spinnerchars=r'\|/-' def __init__(self, steps=20): self.wantsteps=steps self.needrefresh=1 self.filename=None def init(self, name, size=None, cursize=0): self.steps = self.wantsteps self.filename = name if size is None: if name != '' and os.path.isfile(name): size = os.path.getsize(name) self.name = lchoplen(perhaps_showpath(name), scrwidth - self.steps - 4) if not size: #if stdin or device file, we don't know the size, so just use a spinner. If the file is actually zero bytes, it doesn't matter either way. self.stepsize = INF self.steps = 1 elif size<=self.steps: self.stepsize = 1 else: self.stepsize = size/self.steps self.nextstep = self.stepsize self.spinneridx = 0 self.needrefresh = 1 self.update(cursize) def update(self, cursize): if self.needrefresh: donesteps = cursize/self.stepsize stepsleft = self.steps - donesteps self.nextstep = self.stepsize*(donesteps+1) stdprogress.write('%s : %s'%(self.name,'#'*donesteps+'.'*stepsleft)+'\b'*stepsleft); stdprogress.flush() self.needrefresh = 0 elif self.nextstep < cursize: updsteps = (cursize-self.nextstep)/self.stepsize + 1 self.nextstep = self.nextstep + self.stepsize*updsteps stdprogress.write('#'*updsteps); stdprogress.flush() else: stdprogress.write(self.spinnerchars[self.spinneridx]+'\b'); stdprogress.flush() self.spinneridx = (self.spinneridx+1)%len(self.spinnerchars) def cleanup(self): if not self.needrefresh: stdprogress.write('\r'+' '*(len(self.name)+3+self.steps+1)+'\r') self.needrefresh = 1 class TimedProgressMeter(ProgressMeter): def __init__(self, *args, **kw): ProgressMeter.__init__(self, *args, **kw) self.nexttime = 0 def update(self, cursize): curtime = time.time() if curtime > self.nexttime: self.nexttime = curtime + 0.06 ProgressMeter.update(self, cursize) def enverrstr(e): return getattr(e,'strerror',None) or str(e) class CFVException(Exception): pass class CFVValueError(CFVException):#invalid argument in user input pass class CFVNameError(CFVException):#invalid command in user input pass class CFVSyntaxError(CFVException):#error in user input pass class FileInfoCache: def __init__(self): self.data = {} self.stdin = {} self.testfiles = {} def set_testfiles(self, testfiles): for fn in testfiles: fn = os.path.join(reldir[-1], fn) if config.ignorecase: fn = fn.lower() self.testfiles[fn] = 1 def should_test(self, fn): fn = os.path.join(reldir[-1], fn) if config.ignorecase: fn = fn.lower() return self.testfiles.get(fn,0) def set_verified(self, fn): self.getfinfo(fn)['_verified'] = 1 def is_verified(self, fn): return self.getfinfo(fn).get('_verified',0) def set_flag(self, fn, flag): self.getfinfo(fn)[flag] = 1 def has_flag(self, fn, flag): return self.getfinfo(fn).has_key(flag) def getpathcache(self, path): pathkey = get_path_key(path) pathcache = self.data.get(pathkey) if pathcache is None: self.data[pathkey] = pathcache = {} return pathcache def getfinfo(self, fn): if fn=='': return self.stdin else: fpath,ftail = os.path.split(fn) pathdata = self.getpathcache(fpath) finfo = pathdata.get(ftail) if finfo is None: pathdata[ftail] = finfo = {} return finfo def rename(self, oldfn, newfn): ofinfo = self.getfinfo(oldfn) nfinfo = self.getfinfo(newfn) nfinfo.clear() for k,v in ofinfo.items(): if k[0]!="_": #don't preserve flags nfinfo[k]=v #nfinfo.update(ofinfo) ofinfo.clear() def getfilesha1(file): finfo = cache.getfinfo(file) if not finfo.has_key('sha1'): finfo['sha1'],finfo['size'] = _getfilesha1(file) return finfo['sha1'],finfo['size'] def getfilemd5(file): finfo = cache.getfinfo(file) if not finfo.has_key('md5'): finfo['md5'],finfo['size'] = _getfilemd5(file) return finfo['md5'],finfo['size'] def getfilecrc(file): finfo = cache.getfinfo(file) if not finfo.has_key('crc'): finfo['crc'],finfo['size'] = _getfilecrc(file) return finfo['crc'],finfo['size'] def rename(oldfn, newfn): os.rename(oldfn, newfn) cache.rename(oldfn, newfn) class Stats: def __init__(self): self.num=0 self.ok=0 self.badsize=0 self.badcrc=0 self.notfound=0 self.ferror=0 self.cferror=0 self.bytesread=0L #use long int for size, to avoid possible overflow. self.unverified=0 self.diffcase=0 self.misnamed=0 self.quoted=0 self.textmode=0 self.starttime=time.time() self.subcount=0 def make_sub_stats(self): b = copy.copy(self) b.starttime = time.time() return b def sub_stats_end(self, end): for v in 'badcrc', 'badsize', 'bytesread', 'cferror', 'diffcase', 'misnamed', 'ferror', 'notfound', 'num', 'ok', 'quoted', 'unverified', 'textmode': setattr(self, v, getattr(end, v) - getattr(self, v)) end.subcount = end.subcount + 1 def print_stats(self): pinfo('%i files'%self.num,'') pinfo(', %i OK' %self.ok,'') if self.badcrc: pinfo(', %i badcrc' %self.badcrc,'') if self.badsize: pinfo(', %i badsize' %self.badsize,'') if self.notfound: pinfo(', %i not found' %self.notfound,'') if self.ferror: pinfo(', %i file errors' %self.ferror,'') if self.unverified: pinfo(', %i unverified' %self.unverified,'') if self.cferror: pinfo(', %i chksum file errors' %self.cferror,'') if self.misnamed: pinfo(', %i misnamed' %self.misnamed,'') if self.diffcase: pinfo(', %i differing cases' %self.diffcase,'') if self.quoted: pinfo(', %i quoted filenames' %self.quoted,'') if self.textmode: pinfo(', %i tested in textmode' %self.textmode,'') elapsed=time.time()-self.starttime pinfo('. %.3f seconds, '%(elapsed),'') if elapsed==0.0: pinfo('%.1fK'%(self.bytesread/1024.0),'') else: pinfo('%.1fK/s'%(self.bytesread/elapsed/1024.0),'') pinfo('\n','') class Config: verbose=0 # -1=quiet 0=norm 1=noisy docrcchecks=1 dirsort=1 cmdlinesort=1 cmdlineglob='a' recursive=0 showunverified=0 defaulttype='sfv' ignorecase=0 unquote=0 fixpaths=None strippaths=0 showpaths=2 showpathsabsolute=0 gzip=0 rename=0 search=0 renameformat='%(name)s.bad-%(count)i%(ext)s' renameformatnocount=0 list=0 listsep='\n' user_cf_fn_regexs=[] dereference=1 progress='a' announceurl=None piece_size_pow2=18 private_torrent=False def setdefault(self,cftype): if cftype in cftypes.keys(): self.defaulttype=cftype else: raise CFVValueError, "invalid default type '%s'"%cftype def setintr(self,o,v,min,max): try: x=int(v) if x>max or x=0"%v elif o=="fixpaths": self.fixpaths = v and re.compile('['+re.escape(v)+']') or None elif o=="renameformat": testmap=make_rename_formatmap('1.2') testmapwc=make_rename_formatmap('1.2') testmapwc['count']=1 format_test=v%testmapwc try: format_test=v%testmap self.renameformatnocount=1 #if we can get here, it doesn't use the count param except KeyError: self.renameformatnocount=0 self.renameformat=v elif o=="filename_type": typename,match = v.split('=',1) if not cftypes.has_key(typename): raise CFVValueError, "filename_type: invalid type '%s'"%typename self.user_cf_fn_regexs.append((re.compile(match, re.I).search, cftypes[typename])) elif o=='announceurl': self.setstr(o,v) elif o=='piece_size_pow2': self.setintr(o,v,1,30) else: raise CFVNameError, "invalid option '%s'"%o def readconfig(self): filename=os.path.expanduser(os.path.join("~",".cfvrc")) if not os.path.isfile(filename): filename=os.path.expanduser(os.path.join("~","_cfvrc")) if os.path.isfile(filename): file=open(filename,"r") l=0 while 1: l=l+1 s=file.readline() if not s: break #end of file if s[0]=="#": continue #ignore lines starting with # s=chomp(s) if not s: continue #ignore blank lines x = s.split(' ',1) if len(x)!=2: raise CFVSyntaxError, "%s:%i: invalid line '%s'"%(filename,l,s) else: o,v = x try: self.setx(o,v) except CFVException, err: raise sys.exc_info()[0], "%s:%i: %s"%(filename,l,err), sys.exc_info()[2] #reuse the traceback of the original exception, but add file and line numbers to the error def __init__(self): self.readconfig() def make_rename_formatmap(l_filename): sp=os.path.splitext(l_filename) return {'name':sp[0], 'ext':sp[1], 'fullname':l_filename} version='1.18.3'#.1-pre'+'$Revision: 419 $'[11:-2] _hassymlinks=hasattr(os,'symlink') try: from binascii import hexlify, unhexlify # only in python >= 2.0 except ImportError: def hexlify(d): return "%02x"*len(d) % tuple(map(ord, d)) def unhexlify(s, _splitre=re.compile('..',re.DOTALL)): return ''.join(map(chr, map(lambda a: int(a,16), _splitre.findall(s)))) try: if os.environ.get('CFV_NOMMAP'): raise ImportError # mmap is broken in python 2.4.2 and leaks file descriptors if sys.version_info[:3] == (2, 4, 2): raise ImportError import mmap if hasattr(mmap, 'ACCESS_READ'): def dommap(fileno, len):#generic mmap. python2.2 adds ACCESS_* args that work on both nix and win. if len==0: return '' #mmap doesn't like length=0 return mmap.mmap(fileno, len, access=mmap.ACCESS_READ) elif hasattr(mmap, 'PROT_READ'): def dommap(fileno, len):#unix mmap. python default is PROT_READ|PROT_WRITE, but we open readonly. if len==0: return '' #mmap doesn't like length=0 return mmap.mmap(fileno, len, mmap.MAP_SHARED, mmap.PROT_READ) else: def dommap(fileno, len):#windows mmap. if len==0: return '' return mmap.mmap(fileno, len) nommap=0 except ImportError: nommap=1 _MAX_MMAP = 2**32 - 1 _FALLBACK_MMAP = 2**31 - 1 def _getfilechecksum(file, hasher): if file=='': f=sys.stdin else: f=open(file,'rb') def finish(m,s,f=f,file=file): if stdprogress: progress.init(file) try: while 1: x=f.read(65536) if not x: stats.bytesread=stats.bytesread+s return m.digest(),s s=s+len(x) m.update(x) if stdprogress: progress.update(s) finally: if stdprogress: progress.cleanup() if f==sys.stdin or nommap or stdprogress: return finish(hasher(),0L) else: s = os.path.getsize(file) try: if s > _MAX_MMAP: # Work around python 2.[56] problem with md5 of large mmap objects raise OverflowError m = hasher(dommap(f.fileno(), s)) except OverflowError: mmapsize = min(s, _FALLBACK_MMAP) #mmap size is limited by C's int type, which even on 64 bit arches is often 32 bits, so we can't use sys.maxint either. If we get the error, just assume 32 bits. m = hasher(dommap(f.fileno(), mmapsize)) f.seek(mmapsize) return finish(m,mmapsize) #unfortunatly, python's mmap module doesn't support the offset parameter, so we just have to do the rest of the file the old fashioned way. stats.bytesread = stats.bytesread+s return m.digest(),s try: from hashlib import sha1 as sha_new except ImportError: from sha import new as sha_new try: from hashlib import md5 as md5_new except ImportError: from md5 import new as md5_new def _getfilesha1(file): return _getfilechecksum(file, sha_new) try: if os.environ.get('CFV_NOFCHKSUM'): raise ImportError import fchksum try: if fchksum.version()<4:raise ImportError except: stderr.write("old fchksum version installed, using std python modules. please update.\n") #can't use perror yet since config hasn't been done.. raise ImportError def _getfilemd5(file): if stdprogress: progress.init(file) try: c,s=fchksum.fmd5(file, stdprogress and progress.update or None, 0.03) stats.bytesread=stats.bytesread+s finally: if stdprogress: progress.cleanup() return c,s def _getfilecrc(file): if stdprogress: progress.init(file) try: c,s=fchksum.fcrc32d(file, stdprogress and progress.update or None, 0.03) stats.bytesread=stats.bytesread+s finally: if stdprogress: progress.cleanup() return c,s except ImportError: try: import zlib _crc32=zlib.crc32 except ImportError: import binascii _crc32=binascii.crc32 class CRC32: digest_size = 4 def __init__(self, s=''): self.value = _crc32(s) def update(self, s): self.value = _crc32(s, self.value) def digest(self): return struct.pack('>I', self.value & 0xFFFFFFFF) def _getfilemd5(file): return _getfilechecksum(file, md5_new) def _getfilecrc(file): return _getfilechecksum(file, CRC32) def fcmp(f1, f2): import filecmp return filecmp.cmp(f1, f2, shallow=0) try: staticmethod #new in python 2.2 except NameError: class staticmethod: def __init__(self, anycallable): self.__call__ = anycallable class PeekFile: def __init__(self, fileobj, filename=None): self.fileobj = fileobj self.name = filename or fileobj.name def peek(self, *args): self.fileobj.seek(0) return self.fileobj.read(*args) def peekline(self, *args): self.fileobj.seek(0) return self.fileobj.readline(*args) def peeknextline(self, *args): return self.fileobj.readline(*args) def _done_peeking(self): self.fileobj.seek(0) self.peeknextline = None self.peekline = None self.peek = None self.readline = self.fileobj.readline self.read = self.fileobj.read self.seek = self.fileobj.seek def seek(self, *args): self._done_peeking() return self.seek(*args) def readline(self, *args): self._done_peeking() return self.readline(*args) def read(self, *args): self._done_peeking() return self.read(*args) def PeekFileNonseekable(fileobj, filename): import StringIO return PeekFile(StringIO.StringIO(fileobj.read()), filename) def PeekFileGzip(filename): import gzip if filename=='-': import StringIO f = gzip.GzipFile(mode="rb",fileobj=StringIO.StringIO(sys.stdin.read())) #lovely hack since gzip.py requires a bunch of seeking.. bleh. else: f = gzip.open(filename,"rb") try: f.tell() except (AttributeError, IOError): return PeekFileNonseekable(f, filename) #gzip.py prior to python2.2 doesn't support seeking, and prior to 2.0 doesn't support readline(size) else: return PeekFile(f, filename) class NoCloseFile: def __init__(self, fileobj): self.write = fileobj.write self.close = fileobj.flush def doopen_read(filename): mode = 'rb' #read all files in binary mode (since .pars are binary, and we don't always know the filetype when opening, just open everything binary. The text routines should cope with all types of line endings anyway, so this doesn't hurt us.) if config.gzip>=2 or (config.gzip>=0 and filename[-3:].lower()=='.gz'): return PeekFileGzip(filename) else: if filename=='-': return PeekFileNonseekable(sys.stdin, filename) return PeekFile(open(filename, mode)) def doopen_write(filename): mode = 'w' if config.gzip>=2 or (config.gzip>=0 and filename[-3:].lower()=='.gz'): import gzip mode=mode+'b' #open gzip files in binary mode if filename=='-': return gzip.GzipFile(filename=filename, mode=mode, fileobj=sys.stdout) return gzip.open(filename, mode) else: if filename=='-': return NoCloseFile(sys.stdout) return open(filename, mode) def auto_filename_match(*names): for searchfunc, cftype in config.user_cf_fn_regexs + _cf_fn_matches + _cf_fn_exts + _cf_fn_searches: for name in names: if searchfunc(name): return cftype return None def auto_chksumfile_match(file): for p, matchfunc, cftype in _cf_matchers: if matchfunc(file): return cftype return None def register_cftype(name, cftype): cftypes[name] = cftype if hasattr(cftype, 'auto_filename_match'): if cftype.auto_filename_match[-1]=='$' and cftype.auto_filename_match[0]=='^': _cf_fn_matches.append((re.compile(cftype.auto_filename_match, re.I).search, cftype)) elif cftype.auto_filename_match[-1]=='$': _cf_fn_exts.append((re.compile(cftype.auto_filename_match, re.I).search, cftype)) else: _cf_fn_searches.append((re.compile(cftype.auto_filename_match, re.I).search, cftype)) _cf_matchers.append((getattr(cftype,"auto_chksumfile_order", 0), cftype.auto_chksumfile_match, cftype)) _cf_matchers.sort() _cf_matchers.reverse() def parse_commentline(comment, commentchars): if comment[0] in commentchars: return comment[1:].strip() return None class ChksumType: def test_chksumfile(self,file,filename): if config.showunverified: #we can't expect the checksum file itself to be checksummed cache.set_verified(filename) try: if config.verbose>=0 or config.verbose==-3: cf_stats = stats.make_sub_stats() if not file: file=doopen_read(filename) self.do_test_chksumfile(file) if config.verbose>=0 or config.verbose==-3: cf_stats.sub_stats_end(stats) pinfo(perhaps_showpath(file.name)+': ','') cf_stats.print_stats() except EnvironmentError, a: stats.cferror=stats.cferror+1 perror('%s : %s (CF)'%(perhaps_showpath(filename),enverrstr(a))) def do_test_chksumfile_print_testingline(self, file, comment=None): if comment: comment = ', ' + comment if len(comment)>102: #limit the length in case its a really long one. comment = comment[:99]+'...' else: comment = '' pverbose('testing from %s (%s%s)'%(file.name, self.__class__.__name__.lower(), comment)) def do_test_chksumfile(self, file): self.do_test_chksumfile_print_testingline(file) line=1 while 1: l=file.readline() if not l: break if self.do_test_chksumline(l): stats.cferror=stats.cferror+1 perror('%s : unrecognized line %i (CF)'%(perhaps_showpath(file.name),line)) line=line+1 def search_file(self, filename, filecrc, filesize, errfunc, errargs): if (not config.search or (filesize<0 and (not filecrc or not config.docrcchecks))): #don't bother searching if we don't have anything to compare against errfunc(*errargs) return -2 alreadyok=None fpath,filenametail = os.path.split(filename) try: if fpath: if config.ignorecase: fpath = nocase_findfile(fpath, FINDDIR) filename = os.path.join(fpath,filenametail) #fix the dir the orig filename is in, so that the do_f_found can rename it correctly else: fpath = os.curdir ftails = os.listdir(fpath) except EnvironmentError: ftails = [] for ftail in ftails: fn = os.path.join(fpath,ftail) try: if filesize>=0: fs=os.path.getsize(fn) if fs!=filesize: #continue raise EnvironmentError #can't continue in try: until python 2.1+ if config.docrcchecks and filecrc!=None: c = self.do_test_file(fn, filecrc) if c: #continue raise EnvironmentError #can't continue in try: until python 2.1+ filecrct=hexlify(filecrc) else: if not os.path.isfile(fn): #continue raise EnvironmentError #can't continue in try: until python 2.1+ filecrct='exists' except EnvironmentError: continue if cache.has_flag(fn, '_ok'): alreadyok=(fn,filecrct) continue errfunc(foundok=1,*errargs) do_f_found(filename, fn, filesize, filecrct) return 0 if alreadyok: errfunc(foundok=1,*errargs) do_f_found(filename,alreadyok[0],filesize,alreadyok[1],alreadyok=1) return 0 errfunc(*errargs) return -1 def test_file(self,filename,filecrc,filesize=-1): filename = mangle_filename(filename) if cache.testfiles: if not cache.should_test(filename): return stats.num=stats.num+1 l_filename = filename try: l_filename = find_local_filename(filename) if filesize>=0: fs=os.path.getsize(l_filename) if fs!=filesize: self.search_file(filename, filecrc, filesize, do_f_badsize, (l_filename, filesize, fs)) return -2 if config.docrcchecks and filecrc!=None: c=self.do_test_file(l_filename,filecrc) filecrct = hexlify(filecrc) if c: self.search_file(filename, filecrc, filesize, do_f_badcrc, (l_filename, 'crc does not match (%s!=%s)'%(filecrct,hexlify(c)))) return -2 else: if not os.path.exists(l_filename): raise EnvironmentError, (errno.ENOENT,"missing") if not os.path.isfile(l_filename): raise EnvironmentError, (errno.ENOENT,"not a file") filecrct="exists" #since we didn't actually test the crc, make verbose mode merely say it exists except (EnvironmentError, UnicodeError), a: #UnicodeError can occur if python can't map the filename to the filesystem's encoding self.search_file(filename, filecrc, filesize, do_f_enverror, (l_filename, a)) return -1 do_f_ok(l_filename, filesize, filecrct) def make_chksumfile(self, filename): return doopen_write(filename) def make_chksumfile_finish(self, file): pass def do_f_enverror(l_filename, ex, foundok=0): if ex[0]==errno.ENOENT: if foundok: return stats.notfound=stats.notfound+1 if config.list&LISTNOTFOUND: plistf(l_filename) else: #if not foundok: stats.ferror=stats.ferror+1 perror('%s : %s'%(perhaps_showpath(l_filename),enverrstr(ex))) def do_f_badsize(l_filename, expected, actual, foundok=0): if not foundok: stats.badsize=stats.badsize+1 do_f_verifyerror(l_filename, 'file size does not match (%s!=%i)'%(expected,actual), foundok=foundok) def do_f_badcrc(l_filename, msg, foundok=0): if not foundok: stats.badcrc=stats.badcrc+1 do_f_verifyerror(l_filename, msg, foundok=foundok) def do_f_verifyerror(l_filename, a, foundok=0): reninfo='' if not foundok: if config.list&LISTBAD: plistf(l_filename) if config.rename: formatmap=make_rename_formatmap(l_filename) for count in xrange(0,sys.maxint): formatmap['count']=count newfilename=config.renameformat%formatmap if config.renameformatnocount and count>0: newfilename='%s-%i'%(newfilename,count) if l_filename==newfilename: continue #if the filenames are the same they would cmp the same and be deleted. (ex. when renameformat="%(fullname)s") if os.path.exists(newfilename): if fcmp(l_filename, newfilename): os.unlink(l_filename) reninfo=' (dupe of %s removed)'%newfilename break else: rename(l_filename, newfilename) reninfo=' (renamed to %s)'%newfilename break perror('%s : %s%s'%(perhaps_showpath(l_filename),a,reninfo)) def do_f_found(filename, found_fn, filesize, filecrct, alreadyok=0): l_filename = found_fn if config.rename: try: if os.path.exists(filename): verb=None,"fixing name" prep="of" raise EnvironmentError, "File exists" if alreadyok: verb="linked","linking" prep="to" try: os.link(found_fn, filename) except (EnvironmentError, AttributeError), e: if isinstance(e, EnvironmentError) and e[0] not in (errno.EXDEV, errno.EPERM): raise verb="copied","copying" prep="from" import shutil shutil.copyfile(found_fn, filename) else: verb="renamed","renaming" prep="from" rename(found_fn, filename) except EnvironmentError, e: action='but error %r occured %s %s'%(enverrstr(e),verb[1],prep) stats.ferror=stats.ferror+1 else: action='%s %s'%(verb[0],prep) l_filename = filename else: action="found" if config.showunverified: cache.set_verified(l_filename) stats.misnamed=stats.misnamed+1 do_f_ok(filename, filesize, filecrct, msg="OK(%s %s)"%(action,found_fn), l_filename=l_filename) def do_f_ok(filename, filesize, filecrct, msg="OK", l_filename=None): cache.set_flag(l_filename or filename, '_ok') stats.ok=stats.ok+1 if config.list&LISTOK: plistf(filename) if filesize>=0: pverbose('%s : %s (%i,%s)'%(perhaps_showpath(filename),msg,filesize,filecrct)) else: pverbose('%s : %s (%s)'%(perhaps_showpath(filename),msg,filecrct)) #---------- sha1sum ---------- class SHA1_MixIn: def do_test_file(self, filename, filecrc): c=getfilesha1(filename)[0] if c!=filecrc: return c # Base class for md5sum/sha1sum style checksum file formats. class FooSum_Base(ChksumType): def do_test_chksumfile_print_testingline(self, file): ChksumType.do_test_chksumfile_print_testingline(self, file, parse_commentline(file.peekline(128), ';#')) def do_test_chksumline(self, l): if l[0] in ';#': return x=self._foosum_rem.match(l) if not x: return -1 if x.group(2)==' ': if stats.textmode==0: perror('warning: file(s) tested in textmode') stats.textmode = stats.textmode + 1 self.test_file(x.group(3),unhexlify(x.group(1))) class SHA1(FooSum_Base, SHA1_MixIn): description = 'GNU sha1sum' descinfo = 'SHA1,name' def auto_chksumfile_match(file, _autorem=re.compile(r'[0-9a-fA-F]{40} [ *].')): l = file.peekline(4096) while l: if l[0] not in ';#': return _autorem.match(l) is not None l = file.peeknextline(4096) auto_chksumfile_match=staticmethod(auto_chksumfile_match) auto_filename_match = 'sha1' _foosum_rem=re.compile(r'([0-9a-fA-F]{40}) ([ *])([^\r\n]+)[\r\n]*$') def make_std_filename(filename): return filename+'.sha1' make_std_filename = staticmethod(make_std_filename) def make_addfile(self, filename): crc=hexlify(getfilesha1(filename)[0]) return (crc, -1), '%s *%s\n'%(crc,filename) register_cftype('sha1', SHA1) #---------- md5 ---------- class MD5_MixIn: def do_test_file(self, filename, filecrc): c=getfilemd5(filename)[0] if c!=filecrc: return c class MD5(FooSum_Base, MD5_MixIn): description = 'GNU md5sum' descinfo = 'MD5,name' def auto_chksumfile_match(file, _autorem=re.compile(r'[0-9a-fA-F]{32} [ *].')): l = file.peekline(4096) while l: if l[0] not in ';#': return _autorem.match(l) is not None l = file.peeknextline(4096) auto_chksumfile_match=staticmethod(auto_chksumfile_match) auto_filename_match = 'md5' _foosum_rem=re.compile(r'([0-9a-fA-F]{32}) ([ *])([^\r\n]+)[\r\n]*$') def make_std_filename(filename): return filename+'.md5' make_std_filename = staticmethod(make_std_filename) def make_addfile(self, filename): crc=hexlify(getfilemd5(filename)[0]) return (crc, -1), '%s *%s\n'%(crc,filename) register_cftype('md5', MD5) #---------- bsdmd5 ---------- class BSDMD5(ChksumType, MD5_MixIn): description = 'BSD md5' descinfo = 'name,MD5' def auto_chksumfile_match(file, _autorem=re.compile(r'MD5 \(.+\) = [0-9a-fA-F]{32}'+'[\r\n]*$')): return _autorem.match(file.peekline(4096)) is not None auto_chksumfile_match = staticmethod(auto_chksumfile_match) auto_filename_match = '^md5$' _bsdmd5rem=re.compile(r'MD5 \((.+)\) = ([0-9a-fA-F]{32})[\r\n]*$') def do_test_chksumline(self, l): x=self._bsdmd5rem.match(l) if not x: return -1 self.test_file(x.group(1),unhexlify(x.group(2))) def make_std_filename(filename): return filename+'.md5' make_std_filename = staticmethod(make_std_filename) def make_addfile(self, filename): crc=hexlify(getfilemd5(filename)[0]) return (crc, -1), 'MD5 (%s) = %s\n'%(filename, crc) register_cftype('bsdmd5', BSDMD5) #---------- par ---------- def ver2str(v): vers=[] while v or len(vers)<3: vers.insert(0, str(v&0xFF)) v = v >> 8 return '.'.join(vers) try: #support for 64bit ints in struct module was only added in python 2.2 struct.calcsize('< Q') except struct.error: class _Struct: def calcsize(self, fmt, _calcsize=struct.calcsize): return _calcsize(fmt.replace('Q','II')) def unpack(self, fmt, data, _unpack=struct.unpack): unpacked = _unpack(fmt.replace('Q','II'), data) upos = 0 ret = [] for f in fmt.split(' '): if f=='Q': ret.append(long(unpacked[upos])+long(unpacked[upos+1])*2**32L) upos = upos + 2 elif f=='<':pass else: ret.append(unpacked[upos]) upos = upos + 1 return tuple(ret) pack = struct.pack struct = _Struct() class PAR(ChksumType, MD5_MixIn): description = 'Parchive v1 (test-only)' descinfo = 'name,size,MD5' def auto_chksumfile_match(file): return file.peek(8)=='PAR\0\0\0\0\0' auto_chksumfile_match = staticmethod(auto_chksumfile_match) def do_test_chksumfile(self, file): def prog2str(v): return {0x01: 'Mirror', 0x02: 'PAR', 0x03:'SmartPar', 0xFF:'FSRaid'}.get(v, 'unknown(%x)'%v) par_header_fmt = '< 8s I I 16s 16s Q Q Q Q Q Q' par_entry_fmt = '< Q Q Q 16s 16s' par_entry_fmtsize = struct.calcsize(par_entry_fmt) d = file.read(struct.calcsize(par_header_fmt)) magic, version, client, control_hash, set_hash, vol_number, num_files, file_list_ofs, file_list_size, data_ofs, data_size = struct.unpack(par_header_fmt, d) if config.docrcchecks: control_md5 = md5_new() control_md5.update(d[0x20:]) stats.bytesread=stats.bytesread+len(d) if version not in (0x00000900, 0x00010000): #ver 0.9 and 1.0 are the same, as far as we care. Future versions (if any) may very likey have incompatible changes, so don't accept them either. raise EnvironmentError, (errno.EINVAL,"can't handle PAR version %s"%ver2str(version)) pverbose('testing from %s (par v%s, created by %s v%s)'%(file.name,ver2str(version), prog2str(client>>24), ver2str(client&0xFFFFFF))) for i in range(0, num_files): d = file.read(par_entry_fmtsize) size, status, file_size, md5, md5_16k = struct.unpack(par_entry_fmt, d) if config.docrcchecks: control_md5.update(d) d = file.read(size - par_entry_fmtsize) filename = unicode(d, 'utf-16-le') if config.docrcchecks: control_md5.update(d) stats.bytesread=stats.bytesread+size self.test_file(filename, md5, file_size) if config.docrcchecks: while 1: d=file.read(65536) if not d: if control_md5.digest() != control_hash: raise EnvironmentError, (errno.EINVAL,"corrupt par file - bad control hash") break stats.bytesread=stats.bytesread+len(d) control_md5.update(d) #we don't support PAR in create mode, but add these methods so that we can get error messages that are probaby more user friendly. auto_filename_match = 'par$' def make_std_filename(filename): return filename+'.par' make_std_filename = staticmethod(make_std_filename) register_cftype('par', PAR) class PAR2(ChksumType, MD5_MixIn): description = 'Parchive v2 (test-only)' descinfo = 'name,size,MD5' def auto_chksumfile_match(file): return file.peek(8)=='PAR2\0PKT' auto_chksumfile_match = staticmethod(auto_chksumfile_match) def do_test_chksumfile(self, file): pkt_header_fmt = '< 8s Q 16s 16s 16s' pkt_header_size = struct.calcsize(pkt_header_fmt) file_pkt_fmt = '< 16s 16s 16s Q' file_pkt_size = struct.calcsize(file_pkt_fmt) main_pkt_fmt = '< Q I' main_pkt_size = struct.calcsize(main_pkt_fmt) def get_creator(file, pkt_header_fmt=pkt_header_fmt, pkt_header_size=pkt_header_size):#nested scopes not supported until python 2.1 if not config.verbose>0: return None #avoid doing the work if we aren't going to use it anyway. while 1: d = file.read(pkt_header_size) if not d: return None magic, pkt_len, pkt_md5, set_id, pkt_type = struct.unpack(pkt_header_fmt, d) if pkt_type == 'PAR 2.0\0Creator\0': return chompnulls(file.read(pkt_len - pkt_header_size)) else: file.seek(pkt_len - pkt_header_size, 1) self.do_test_chksumfile_print_testingline(file, get_creator(file)) file.seek(0) # reset file position after looking for creator packet seen_file_ids = {} expected_file_ids = None while 1: d = file.read(pkt_header_size) if not d: break magic, pkt_len, pkt_md5, set_id, pkt_type = struct.unpack(pkt_header_fmt, d) if config.docrcchecks: control_md5 = md5_new() control_md5.update(d[0x20:]) stats.bytesread=stats.bytesread+len(d) d = file.read(pkt_len - pkt_header_size) control_md5.update(d) stats.bytesread=stats.bytesread+len(d) if control_md5.digest() != pkt_md5: raise EnvironmentError, (errno.EINVAL,"corrupt par2 file - bad packet hash") if pkt_type == 'PAR 2.0\0FileDesc': if not config.docrcchecks: d = file.read(pkt_len - pkt_header_size) file_id, file_md5, file_md5_16k, file_size = struct.unpack(file_pkt_fmt, d[:file_pkt_size]) if seen_file_ids.get(file_id) is None: seen_file_ids[file_id] = 1 filename = chompnulls(d[file_pkt_size:]) self.test_file(filename, file_md5, file_size) elif pkt_type == "PAR 2.0\0Main\0\0\0\0": if not config.docrcchecks: d = file.read(pkt_len - pkt_header_size) if expected_file_ids is None: expected_file_ids = [] slice_size, num_files = struct.unpack(main_pkt_fmt, d[:main_pkt_size]) num_nonrecovery = (len(d)-main_pkt_size)/16 - num_files for i in range(main_pkt_size,main_pkt_size+(num_files+num_nonrecovery)*16,16): expected_file_ids.append(d[i:i+16]) else: if not config.docrcchecks: file.seek(pkt_len - pkt_header_size, 1) if expected_file_ids is None: raise EnvironmentError, (errno.EINVAL,"corrupt or unsupported par2 file - no main packet found") for id in expected_file_ids: if not seen_file_ids.has_key(id): raise EnvironmentError, (errno.EINVAL,"corrupt or unsupported par2 file - expected file description packet not found") #we don't support PAR2 in create mode, but add these methods so that we can get error messages that are probaby more user friendly. auto_filename_match = 'par2$' def make_std_filename(filename): return filename+'.par2' make_std_filename = staticmethod(make_std_filename) register_cftype('par2', PAR2) #---------- .torrent ---------- _btimporterror = None try: from BTL import bencode, btformats except ImportError, e1: try: from BitTorrent import bencode, btformats except ImportError, e2: try: from BitTornado import bencode; from BitTornado.BT1 import btformats except ImportError, e3: _btimporterror = '%s and %s and %s'%(e1,e2,e3) class Torrent(ChksumType): description = 'BitTorrent metainfo' descinfo = 'name,size,SHA1(piecewise)' def auto_chksumfile_match(file): return file.peek(1)=='d' and file.peek(4096).find('8:announce')>=0 auto_chksumfile_match = staticmethod(auto_chksumfile_match) def do_test_chksumfile(self, file): if _btimporterror: raise EnvironmentError, _btimporterror try: metainfo = bencode.bdecode(file.read()) btformats.check_message(metainfo) except ValueError, e: raise EnvironmentError, str(e) or 'invalid or corrupt torrent' comments = [] if metainfo.has_key('creation date'): try: comments.append('created '+time.ctime(metainfo['creation date'])) except TypeError: comments.append('created '+repr(metainfo['creation date'])) if metainfo.has_key('comment'): comments.append(metainfo['comment']) self.do_test_chksumfile_print_testingline(file, ', '.join(comments)) encoding = metainfo.get('encoding') def init_file(filenameparts, ftotpos, filesize, encoding=encoding, file=file, self=self): if encoding: try: filenameparts = map(lambda p,encoding=encoding: unicode(p,encoding), filenameparts) except LookupError, e: #lookup error is raised when specified encoding isn't found. raise EnvironmentError, str(e) except UnicodeError, e: stats.cferror=stats.cferror+1 perror('%s : error decoding filename %r : %s (CF)'%(perhaps_showpath(file.name),filenameparts,str(e))) #here we fall through and just let it use the undecoded filename.. is that really ok? filename = os.path.join(*filenameparts) if not config.docrcchecks: #if we aren't testing checksums, just use the standard test_file function, so that -s and such will work. self.test_file(filename, None, filesize) return filename = mangle_filename(filename) done = cache.testfiles and not cache.should_test(filename) #if we don't want to test this file, just pretending its done already has the desired effect. if not done: stats.num=stats.num+1 try: l_filename = find_local_filename(filename) if not os.path.exists(l_filename): raise EnvironmentError, (errno.ENOENT,"missing") if not os.path.isfile(l_filename): raise EnvironmentError, (errno.ENOENT,"not a file") fs=os.path.getsize(l_filename) if fs!=filesize: if not done: do_f_badsize(l_filename, filesize, fs) done=1 except (EnvironmentError, UnicodeError), e: #UnicodeError can occur if python can't map the filename to the filesystem's encoding if not done: do_f_enverror(filename, e) done=1 l_filename = None return Data(totpos=ftotpos, size=filesize, filename=filename, l_filename=l_filename, done=done) info = metainfo['info'] piecelen = info['piece length'] hashes = re.compile('.'*20, re.DOTALL).findall(info['pieces']) if info.has_key('length'): total_len = info['length'] files = [init_file([info['name']], 0, total_len)] else: dirpart = [info['name']] if config.strippaths==0: dirname = info['name'] if encoding: try: dirname = unicode(dirname, encoding) except (LookupError, UnicodeError): #lookup error is raised when specified encoding isn't found. pass dirname = strippath(dirname, 0) if config.ignorecase: try: nocase_findfile(dirname,FINDDIR) except EnvironmentError: dirpart = [] else: if not os.path.isdir(dirname): dirpart = [] #if not dirpart: # pinfo("enabling .torrent path strip hack") files = [] total_len = 0 for finfo in info['files']: flength = finfo['length'] files.append(init_file(dirpart+finfo['path'], total_len, flength)) total_len = total_len + flength if not config.docrcchecks: return curfh = Data(fh=None, f=None) def readfpiece(f, pos, size, curfh=curfh): if f.l_filename is None: return None try: if not curfh.fh or curfh.f is not f: if curfh.fh: curfh.fh.close() curfh.fh = open(f.l_filename,'rb') curfh.f = f curfh.fh.seek(pos) d = curfh.fh.read(size) except EnvironmentError, e: if not f.done: if stdprogress: progress.cleanup() do_f_enverror(f.l_filename, e) f.done=1 return None stats.bytesread=stats.bytesread+len(d) if len(d)= files[curf].totpos+files[curf].size: curf = curf + 1 f=files[curf] fcurpos = curpos-f.totpos assert fcurpos >= 0 fpiecelen = min(curend-curpos, f.size-fcurpos) piecefiles.append((f,fcurpos,fpiecelen)) curpos = curpos + fpiecelen if not f.done: wanttest = 1 if wanttest: sh = sha_new() for f,fcurpos,fpiecelen in piecefiles: if stdprogress and f.l_filename and f.l_filename!=progress.filename: progress.cleanup() progress.init(f.l_filename,f.size,fcurpos) d = readfpiece(f, fcurpos, fpiecelen) if d is None: break sh.update(d) if stdprogress and f.l_filename: progress.update(fcurpos+fpiecelen) if d is None: if curfh.fh: #close the file in case do_f_badcrc wants to try to rename it curfh.fh.close(); curfh.fh=None for f,fcurpos,fpiecelen in piecefiles: if not f.done: if stdprogress: progress.cleanup() do_f_badcrc(f.l_filename, 'piece %i missing data'%(piece)) f.done=1 elif sh.digest()!=hashes[piece]: if curfh.fh: #close the file in case do_f_badcrc wants to try to rename it curfh.fh.close(); curfh.fh=None for f,fcurpos,fpiecelen in piecefiles: if not f.done: if stdprogress: progress.cleanup() do_f_badcrc(f.l_filename, 'piece %i (at %i..%i) crc does not match (%s!=%s)'%(piece,fcurpos,fcurpos+fpiecelen,hexlify(hashes[piece]), sh.hexdigest())) f.done=1 else: for f,fcurpos,fpiecelen in piecefiles: if not f.done: if fcurpos+fpiecelen==f.size: if stdprogress: progress.cleanup() do_f_ok(f.l_filename,f.size,'all pieces ok') f.done=1 if stdprogress: progress.cleanup() if curfh.fh: curfh.fh.close() for f in files: if not f.done: do_f_ok(f.l_filename,f.size,'all pieces ok') auto_filename_match = 'torrent$' def make_std_filename(filename): return filename+'.torrent' make_std_filename = staticmethod(make_std_filename) def make_chksumfile(self, filename): if _btimporterror: raise EnvironmentError, _btimporterror if config.announceurl==None: raise EnvironmentError, 'announce url required' file = ChksumType.make_chksumfile(self, filename) self.sh = sha_new() self.files = [] self.pieces = [] self.piece_done = 0 self.piece_length = 2**config.piece_size_pow2 return file def make_addfile(self, filename): firstpiece = len(self.pieces) f = open(filename, 'rb') if stdprogress: progress.init(filename) fs = 0 while 1: piece_left = self.piece_length - self.piece_done d = f.read(piece_left) if not d: break self.sh.update(d) s = len(d) stats.bytesread=stats.bytesread+s fs = fs + s if stdprogress: progress.update(fs) self.piece_done = self.piece_done + s if self.piece_done == self.piece_length: self.pieces.append(self.sh.digest()) self.sh = sha_new() self.piece_done = 0 f.close() if stdprogress: progress.cleanup() self.files.append({'length':fs, 'path':path_split(filename)}) return ('pieces %i..%i'%(firstpiece,len(self.pieces)),fs), '' def make_chksumfile_finish(self, file): if self.piece_done > 0: self.pieces.append(self.sh.digest()) info = {'pieces':''.join(self.pieces), 'piece length':self.piece_length} if config.private_torrent: info['private'] = 1 if len(self.files)==1 and len(self.files[0]['path'])==1: info['name'],info['length'] = self.files[0]['path'][0],self.files[0]['length'] else: commonroot = self.files[0]['path'][0] for fileinfo in self.files[1:]: if commonroot!=fileinfo['path'][0]: commonroot = None break if commonroot: for fileinfo in self.files: del fileinfo['path'][0] else: commonroot = os.path.split(os.getcwd())[1] info['files'] = self.files info['name'] = commonroot btformats.check_info(info) data = {'info': info, 'announce': config.announceurl.strip(), 'creation date': long(time.time())} #if comment: # data['comment'] = comment file.write(bencode.bencode(data)) register_cftype('torrent', Torrent) #---------- sfv ---------- class CRC_MixIn: def do_test_file(self, filename, filecrc): c=getfilecrc(filename)[0] if c!=filecrc: return c class SFV_Base(ChksumType): description = 'Simple File Verify' def do_test_chksumfile_print_testingline(self, file): #override the default testing line to show first SFV comment line, if any ChksumType.do_test_chksumfile_print_testingline(self, file, parse_commentline(file.peekline(128), ';')) def do_test_chksumline(self, l): if l[0]==';': return x=self._sfvrem.match(l) if not x: return -1 self.test_file(x.group(1),unhexlify(x.group(2))) def make_chksumfile(self, filename): file = ChksumType.make_chksumfile(self, filename) file.write('; Generated by cfv v%s on %s\n;\n'%(version,time.strftime('%Y-%m-%d at %H:%M.%S',time.gmtime(time.time())))) return file class SFV(SFV_Base, CRC_MixIn): descinfo = 'name,CRC32' def auto_chksumfile_match(file, _autorem=re.compile('.+ [0-9a-fA-F]{8}[\n\r]*$')): l = file.peekline(4096) while l: if l[0]!=';': return _autorem.match(l) is not None l = file.peeknextline(4096) auto_chksumfile_match = staticmethod(auto_chksumfile_match) auto_filename_match = 'sfv$' _sfvrem=re.compile(r'(.+) ([0-9a-fA-F]{8})[\r\n]*$') def make_std_filename(filename): return filename+'.sfv' make_std_filename = staticmethod(make_std_filename) def make_addfile(self, filename): crc=hexlify(getfilecrc(filename)[0]) return (crc, -1), '%s %s\n'%(filename,crc) register_cftype('sfv', SFV) class SFVMD5(SFV_Base, MD5_MixIn): descinfo = 'name,MD5' def auto_chksumfile_match(file, _autorem=re.compile('.+ [0-9a-fA-F]{32}[\n\r]*$')): l = file.peekline(4096) while l: if l[0]!=';': return _autorem.match(l) is not None l = file.peeknextline(4096) auto_chksumfile_match = staticmethod(auto_chksumfile_match) auto_chksumfile_order = -1 # do -1 prio since we can mistakenly match a bsdmd5 file #auto_filename_match = 'md5$' #hm. People are probably used to .md5 making a md5sum format file, so we can't just do this. _sfvrem=re.compile(r'(.+) ([0-9a-fA-F]{32})[\r\n]*$') def make_std_filename(filename): return filename+'.md5' make_std_filename = staticmethod(make_std_filename) def make_addfile(self, filename): crc=hexlify(getfilemd5(filename)[0]) return (crc, -1), '%s %s\n'%(filename,crc) register_cftype('sfvmd5', SFVMD5) #---------- csv ---------- _csvstrautore=r'(?:".*"|[^,]*),' _csvstrre=r'(?:"(.*)"|([^,]*)),' _csvfnautore=_csvstrautore.replace('*','+') _csvfnre=_csvstrre.replace('*','+') def csvquote(s): if ',' in s or '"' in s: return '"%s"'%(s.replace('"','""')) return s def csvunquote(g1,g2): if g1 is not None: return g1.replace('""','"') return g2 class CSV(ChksumType, CRC_MixIn): description = 'Comma Separated Values' descinfo = 'name,size,CRC32' def auto_chksumfile_match(file, _autorem=re.compile(_csvfnautore+'[0-9]+,[0-9a-fA-F]{8},[\n\r]*$')): return _autorem.match(file.peekline(4096)) is not None auto_chksumfile_match = staticmethod(auto_chksumfile_match) auto_filename_match = 'csv$' _csvrem=re.compile(_csvfnre+r'([0-9]+),([0-9a-fA-F]{8}),') def do_test_chksumline(self, l): x=self._csvrem.match(l) if not x: return -1 self.test_file(csvunquote(x.group(1),x.group(2)), unhexlify(x.group(4)), int(x.group(3))) def make_std_filename(filename): return filename+'.csv' make_std_filename = staticmethod(make_std_filename) def make_addfile(self, filename): c,s=getfilecrc(filename) c = hexlify(c) return (c,s), '%s,%i,%s,\n'%(csvquote(filename),s,c) register_cftype('csv', CSV) #---------- csv with 4 fields ---------- class CSV4(ChksumType, CRC_MixIn): description = 'Comma Separated Values' descinfo = 'name,size,CRC32,path' def auto_chksumfile_match(file, _autorem=re.compile(r'%s[0-9]+,[0-9a-fA-F]{8},%s[\n\r]*$'%(_csvfnautore,_csvstrautore))): return _autorem.match(file.peekline(4096)) is not None auto_chksumfile_match = staticmethod(auto_chksumfile_match) _csv4rem=re.compile(r'%s([0-9]+),([0-9a-fA-F]{8}),%s'%(_csvfnre,_csvstrre)) def do_test_chksumline(self, l): x=self._csv4rem.match(l) if not x: return -1 name = csvunquote(x.group(1),x.group(2)) path = csvunquote(x.group(5),x.group(6)) self.test_file(os.path.join(fixpath(path),name),unhexlify(x.group(4)),int(x.group(3))) #we need to fixpath before path.join since os.path.join looks for path.sep def make_std_filename(filename): return filename+'.csv' make_std_filename = staticmethod(make_std_filename) def make_addfile(self, filename): c,s=getfilecrc(filename) c = hexlify(c) p=os.path.split(filename) return (c,s), '%s,%i,%s,%s,\n'%(csvquote(p[1]),s,c,csvquote(p[0])) register_cftype('csv4', CSV4) #---------- csv with only 2 fields ---------- class CSV2(ChksumType): description = 'Comma Separated Values' descinfo = 'name,size' def auto_chksumfile_match(file, _autorem=re.compile(_csvfnautore+'[0-9]+,[\n\r]*$')): return _autorem.match(file.peekline(4096)) is not None auto_chksumfile_match = staticmethod(auto_chksumfile_match) _csv2rem=re.compile(_csvfnre+r'([0-9]+),') def do_test_chksumline(self, l): x=self._csv2rem.match(l) if not x: return -1 self.test_file(csvunquote(x.group(1),x.group(2)),None,int(x.group(3))) def make_std_filename(filename): return filename+'.csv' make_std_filename = staticmethod(make_std_filename) def make_addfile(self, filename): if filename=='': s=getfilecrc(filename)[1]#no way to get size of stdin other than to read it else: s=os.path.getsize(filename) return (None, s), '%s,%i,\n'%(csvquote(filename),s) register_cftype('csv2', CSV2) #---------- jpegsheriff .crc ---------- def getimagedimensions(filename): if filename == '': return '0','0' try: import Image im1=Image.open(filename) return map(str, im1.size) except (ImportError, IOError): return '0','0' def commaize(n): n = str(n) s = n[-3:] n = n[:-3] while n: s = n[-3:]+','+s n = n[:-3] return s class JPEGSheriff_CRC(ChksumType, CRC_MixIn): description = 'JPEGSheriff' descinfo = 'name,size,dimensions,CRC32' def auto_chksumfile_match(file, _autorem=re.compile(r'^Filename\s+(Filesize\s+)?.*?CRC-?32.*^-+(\s+-+){1,4}\s*$', re.DOTALL|re.IGNORECASE|re.MULTILINE)): return _autorem.search(file.peek(1024)) is not None auto_chksumfile_match = staticmethod(auto_chksumfile_match) auto_filename_match = 'crc$' def do_test_chksumfile_print_testingline(self, file): x=re.search(r'^Filename\s+(Filesize\s+)?(.+?)??CRC-?32.*^-+(\s+-+){1,4}\s*$', file.peek(1024), re.DOTALL|re.IGNORECASE|re.MULTILINE) self.has_size = x.group(1) is not None self.has_dimensions = x.group(2) is not None crcre = r'(?P.*\S)\s+' if self.has_size: crcre = crcre + r'(?P[0-9.,]+)\s+' if self.has_dimensions: crcre = crcre + r'\S+\s*x\s*\S+\s+' crcre = crcre + r'(?P[0-9a-fA-F]{8})\b' self._crcrem = re.compile(crcre, re.I) self.in_comments = 1 #override the default testing line to show first comment line, if any comment = ', ' + file.peekline().strip() if len(comment)>102: #but limit the length in case its a really long one. comment = comment[:99]+'...' pverbose('testing from %s (crc%s)'%(file.name, comment)) _commentboundary=re.compile(r'^-+(\s+-+){1,4}\s*$') _nstrip=re.compile(r'[.,]') def do_test_chksumline(self, l): if self._commentboundary.match(l): self.in_comments = not self.in_comments return x=self._crcrem.match(l) if not x: if self.in_comments: return return -1 if self.has_size: size = int(self._nstrip.sub('',x.group('size'))) else: size = -1 self.test_file(x.group('name'), unhexlify(x.group('crc')), size) def make_std_filename(filename): return filename+'.crc' make_std_filename = staticmethod(make_std_filename) def make_chksumfile(self, filename): file = ChksumType.make_chksumfile(self, filename) file.write('Generated by: cfv (v%s)\n'%(version)) file.write('Generated at: %s\n'%time.strftime('%a, %d %b %Y %H:%M:%S',time.gmtime(time.time()))) file.write('Find it at: http://cfv.sourceforge.net/\n\n') try: import Image self.use_dimensions=1 ####should this be made optional somehow? except ImportError: self.use_dimensions=0 self._flist = [] self._fieldlens = [0]*6 self._ftotal = 0L return file def make_chksumfile_finish(self, file): flens = self._fieldlens def writedata(data, self=self, flens=flens, file=file): file.write(data[0].ljust(flens[0]) + ' ' + data[1].rjust(flens[1])) if self.use_dimensions: file.write(' ' + data[2].rjust(flens[2]) + ' x ' + data[3].rjust(flens[3])) file.write(' ' + data[4].rjust(flens[4]) + ' ' + data[5] + '\n') header = ('Filename', ' Filesize ', ' W', 'H '.ljust(flens[3]), ' CRC-32 ', 'Description') for i in range(0, len(header)-1): self._fieldlens[i] = max(self._fieldlens[i], len(header[i])) boundary = '-'*flens[0] + ' ' + '-'*flens[1] + ' ' if self.use_dimensions: boundary = boundary + '-'*(flens[2]+flens[3]+3) + ' ' boundary = boundary + '-'*flens[4] + ' ' + '-'*flens[5] + '\n' writedata(header) file.write(boundary) for fdata in self._flist: writedata(fdata) file.write(boundary) file.write('\nCount of files: %i\n'%len(self._flist)) file.write('Total of sizes: %s\n'%commaize(self._ftotal)) def make_addfile(self, filename): crc,size=getfilecrc(filename) crc = hexlify(crc) if self.use_dimensions: w,h = getimagedimensions(filename) else: w=h='' fdata = (filename, commaize(size), w, h, crc, '') for i in range(0, len(fdata)): self._fieldlens[i] = max(self._fieldlens[i], len(fdata[i])) self._flist.append(fdata) self._ftotal = self._ftotal + size #we cheat and do all writing in make_chksumfile_finish since it needs to be properly formatted, and we can't do that until we have seen the max lengths for all fields return (crc, size), '' register_cftype('crc', JPEGSheriff_CRC) #---------- generic ---------- def perhaps_showpath(file): if config.showpaths==1 or (config.showpaths==2 and config.recursive): if config.showpathsabsolute: dir=curdir else: dir=reldir[-1] return os.path.join(dir,file) return file _nocase_dir_cache = {} def nocase_dirfiles(dir,match): "return list of filenames in dir whose lowercase value equals match" dirkey=get_path_key(dir) if not _nocase_dir_cache.has_key(dirkey): d={} _nocase_dir_cache[dirkey]=d for a in os.listdir(dir): l=a.lower() if d.has_key(l): d[l].append(a) else: d[l]=[a] else: d=_nocase_dir_cache[dirkey] if d.has_key(match): return d[match] return [] def path_split(filename): "returns a list of components of filename" head=filename parts=[] while 1: head,tail=os.path.split(head) if tail: parts.insert(0,tail) else: if head: parts.insert(0,head) break return parts FINDFILE=1 FINDDIR=0 def nocase_findfile(filename,find=FINDFILE): cur=os.curdir parts=path_split(filename.lower()) #print 'nocase_findfile:',filename,parts,len(parts) for i in range(0,len(parts)): p=parts[i] #matches=filter(lambda f,p=p: string.lower(f)==p,dircache.listdir(cur)) #too slooow, even with dircache (though not as slow as without it ;) matches=nocase_dirfiles(cur,p) #nice and speedy :) #print 'i:',i,' cur:',cur,' p:',p,' matches:',matches if i==len(parts)-find:#if we are on the last part of the path and using FINDFILE, we want to match a file matches=filter(lambda f,cur=cur: os.path.isfile(os.path.join(cur,f)), matches) else:#otherwise, we want to match a dir matches=filter(lambda f,cur=cur: os.path.isdir(os.path.join(cur,f)), matches) if not matches: raise IOError, (errno.ENOENT,os.strerror(errno.ENOENT)) if len(matches)>1: raise IOError, (errno.EEXIST,"More than one name matches %s"%os.path.join(cur,p)) if cur==os.curdir: cur=matches[0] #don't put the ./ on the front of the name else: cur=os.path.join(cur,matches[0]) return cur def nocase_findfile_updstats(filename): cur = nocase_findfile(filename) if filename != cur: stats.diffcase = stats.diffcase + 1 return cur def strippath(filename, num='a', _splitdrivere=re.compile(r"[a-z]:[/\\]",re.I)): if num=='a':#split all the path off return os.path.split(filename)[1] if num=='n':#split none of the path return filename if _splitdrivere.match(filename,0,3): #we can't use os.path.splitdrive, since we want to get rid of it even if we are not on a dos system. filename=filename[3:] if filename[0]==os.sep: filename=lstrip(filename, os.sep) if num==0:#only split drive letter/root slash off return filename parts = path_split(filename) if len(parts) <= num: return parts[-1] return os.path.join(*parts[num:]) _os_sep_escaped = os.sep.replace('\\','\\\\') def fixpath(filename): if config.fixpaths: return config.fixpaths.sub(_os_sep_escaped, filename) return filename def mangle_filename(filename): if config.unquote and len(filename)>1 and filename[0]=='"' and filename[-1]=='"': filename = filename[1:-1] #work around buggy sfv encoders that quote filenames stats.quoted = stats.quoted + 1 if config.fixpaths: filename=fixpath(filename) filename=os.path.normpath(filename) if config.strippaths!='n': filename=strippath(filename, config.strippaths) return filename def find_local_filename(l_filename): if config.ignorecase: #we need to find the correct filename if using showunverified, even if the filename we are given works, since on FAT/etc filesystems the incorrect case will still return true from os.path.exists, but it could be the incorrect case to remove from the unverified files list. if config.showunverified or not os.path.exists(l_filename): l_filename=nocase_findfile_updstats(l_filename) if config.showunverified: cache.set_verified(l_filename) return l_filename _visited_dirs = {} def visit_dir(name, st=None, noisy=1): if not config.dereference and os.path.islink(name): return 0 if st is None: try: st = os.stat(name) except os.error: return 0 if S_ISDIR(st[ST_MODE]): #the inode check is kinda a hack, but systems that don't have inode numbers probably don't have symlinks either. if config.dereference and st[ST_INO]: dir_key = (st[ST_DEV], st[ST_INO]) if _visited_dirs.has_key(dir_key): if noisy: perror("skipping already visited dir %s %s"%(perhaps_showpath(name), dir_key)) return 0 _visited_dirs[dir_key] = 1 return 1 return 0 def test(filename, typename, restrict_typename='auto'): if typename != 'auto': cf = cftypes[typename]() cf.test_chksumfile(None, filename) return try: file=doopen_read(filename) cftype = auto_chksumfile_match(file) if restrict_typename != 'auto' and cftypes[restrict_typename] != cftype: return if cftype: cf = cftype() cf.test_chksumfile(file, filename) return except EnvironmentError, a: stats.cferror=stats.cferror+1 perror('%s : %s (CF)'%(perhaps_showpath(filename),enverrstr(a))) return -1 perror("I don't recognize the type of %s"%filename) stats.cferror=stats.cferror+1 def make(cftype,ifilename,testfiles): file=None if ifilename: filename=ifilename else: filename=cftype.make_std_filename(os.path.basename(curdir)) if config.gzip==1 and filename[-3:]!='.gz': #if user does -zz, perhaps they want to force the filename to be kept? filename=filename+'.gz' if not hasattr(cftype, "make_addfile"): perror("%s : %s not supported in create mode"%(filename, cftype.__name__.lower())) stats.cferror=stats.cferror+1 return if os.path.exists(filename): perror("%s already exists"%perhaps_showpath(filename)) stats.cferror=stats.cferror+1 file=IOError #just need some special value to indicate a cferror so that recursive mode still continues to work, IOError seems like a good choice ;) if not testfiles: tfauto=1 testfiles=os.listdir(os.curdir) if config.dirsort: testfiles.sort() else: tfauto=0 testdirs=[] if config.verbose>=0 or config.verbose==-3: cf_stats = stats.make_sub_stats() i=0 while i=0: pverbose('%s : OK (%i,%s)'%(perhaps_showpath(f),filesize,filecrc)) else: pverbose('%s : OK (%s)'%(perhaps_showpath(f),filecrc)) stats.ok=stats.ok+1 if file and file!=IOError: try: cf.make_chksumfile_finish(file) file.close() except EnvironmentError, a: stats.cferror=stats.cferror+1 perror('%s : %s (CF)'%(perhaps_showpath(filename),enverrstr(a))) else: if config.verbose>=0 or config.verbose==-3: cf_stats.sub_stats_end(stats) pinfo(perhaps_showpath(filename)+': ','') cf_stats.print_stats() for f in testdirs: try: chdir(f) except EnvironmentError, a: perror('%s%s : %s'%(f, os.sep, enverrstr(a))) stats.ferror=stats.ferror+1 else: make(cftype,ifilename,None) cdup() def print_unverified(file): perror('%s : not verified'%perhaps_showpath(file)) def show_unverified_file(file): unverified_file(file) print_unverified(file) def unverified_file(file): if config.list&LISTUNVERIFIED: plistf(file) stats.unverified=stats.unverified+1 def show_unverified_dir(path, unvchild=0): pathcache = cache.getpathcache(path) pathfiles = os.listdir(path or os.curdir) vsub = 0 unvsave = stats.unverified unv = 0 unv_sub_dirs = [] for fn in pathfiles: file = os.path.join(path, fn) try: st = os.stat(file) if S_ISDIR(st[ST_MODE]) and visit_dir(file,st,noisy=0): dunvsave = stats.unverified dv = show_unverified_dir(file,not pathcache) vsub = vsub + dv if stats.unverified - dunvsave and not dv: #if this directory (and its subdirs) had unverified files and no verified files unv_sub_dirs.append(file) elif pathcache: if not pathcache.get(fn,{}).get('_verified'): if S_ISREG(st[ST_MODE]): show_unverified_file(file) else: if S_ISREG(st[ST_MODE]): unverified_file(file) unv = unv + 1 except OSError: pass if not pathcache and pathfiles: if vsub: # if sub directories do have verified files if unv: # and this directory does have unverified files print_unverified(os.path.join(path,'*')) for file in unv_sub_dirs: # print sub dirs that had unverified files and no verified files print_unverified(os.path.join(file,'**')) elif not unvchild: # if this is the root of a tree with no verified files if stats.unverified - unvsave: # and there were unverified files in the tree print_unverified(os.path.join(path,'**')) return vsub + (not not pathcache) def show_unverified_dir_verbose(path): pathcache = cache.getpathcache(path) pathfiles = os.listdir(path or os.curdir) for fn in pathfiles: file = os.path.join(path, fn) try: st = os.stat(file) if S_ISDIR(st[ST_MODE]) and visit_dir(file,st,noisy=0): show_unverified_dir_verbose(file) elif not pathcache.get(fn,{}).get('_verified'): if S_ISREG(st[ST_MODE]): show_unverified_file(file) except OSError,e: pass def show_unverified_files(filelist): if not config.showunverified: return if filelist: for file in filelist: if config.ignorecase: try: file = nocase_findfile(file) except IOError: continue else: if not os.path.isfile(file): continue if not cache.is_verified(file): show_unverified_file(file) else: _visited_dirs.clear() if config.showunverified==2: show_unverified_dir_verbose('') else: show_unverified_dir('') atrem=re.compile(r'md5|sha1|\.(csv|sfv|par|p[0-9][0-9]|par2|torrent|crc)(\.gz)?$',re.IGNORECASE)#md5sum/sha1sum files have no standard extension, so just search for files with md5/sha1 in the name anywhere, and let the test func see if it really is one. def autotest(typename): for a in os.listdir(os.curdir): if config.recursive and visit_dir(a): try: chdir(a) except EnvironmentError, e: perror('%s%s : %s'%(a, os.sep, enverrstr(e))) stats.ferror=stats.ferror+1 else: autotest(typename) cdup() if atrem.search(a): test(a, 'auto', typename) def printusage(err=0): phelp = err and perror or pinfo phelp('Usage: cfv [opts] [-p dir] [-T|-C] [-t type] [-f file] [files...]') phelp(' -r recursive mode 1 (make seperate chksum files for each dir)') phelp(' -rr recursive mode 2 (make a single file with deep listing in it)') phelp(' -R not recursive (default)') if _hassymlinks: phelp(' -l follow symlinks (default)') phelp(' -L don\'t follow symlinks') phelp(' -T test mode (default)') phelp(' -C create mode') phelp(' -t set type to (%s, or auto(default))'%', '.join(sorted(cftypes.keys()))) phelp(' -f use as list file') phelp(' -m check only for missing files (don\'t compare checksums)') phelp(' -M check checksums (default)') phelp(' -n rename bad files') phelp(' -N don\'t rename bad files (default)') phelp(' -s search for correct file') phelp(' -S don\'t search for correct file (default)') phelp(' -p change to directory before doing anything') phelp(' -i ignore case') phelp(' -I don\'t ignore case (default)') phelp(' -u show unverified files') phelp(' -U don\'t show unverified files (default)') phelp(' -v verbose') phelp(' -V not verbose (default)') phelp(' -VV don\'t print status line at end either') phelp(' -q quiet mode. check exit code for success.') phelp(' -Q mostly quiet mode. only prints status lines.') phelp(' -zz force making gzipped files, even if not ending in .gz') phelp(' -z make gzipped files in auto create mode') phelp(' -Z don\'t create gzipped files automatically. (default)') phelp(' -ZZ never use gzip, even if file ends in .gz') phelp(' --list= raw list files of type (%s)'%', '.join(LISTARGS.keys())) phelp(' --list0= same as list, but seperate files with nulls (useful for xargs -0)') phelp(' --unquote=VAL handle checksum files with quoted filenames (yes or no(default))') phelp(' --fixpaths= replace any chars in with %s'%os.sep) phelp(' --strippaths=VAL strip leading components from file names.') phelp(' --showpaths=

show full paths (none/auto/yes-absolute/relative)') phelp(' --renameformat= format string to use with -n option') phelp(' --progress=VAL show progress meter (yes, no, or auto(default))') phelp(' --help/-h show help') phelp(' --version show cfv and module versions') phelp('torrent creation options:') phelp(' --announceurl=URL tracker announce url') phelp(' --piece_size_pow2=N power of two to set the piece size to (default 18)') phelp(' --private_torrent set private flag in torrent') sys.exit(err) def printhelp(): pinfo('cfv v%s - Copyright (C) 2000-2009 Matthew Mueller - GPL license'%version) printusage() def printcftypehelp(err): phelp = err and perror or pinfo phelp('Valid types:') def printtypeinfo(typename, info, desc, phelp=phelp): phelp(' %-8s %-26s %s'%(typename,desc,info)) printtypeinfo('TYPE', 'FILE INFO STORED', 'DESCRIPTION') printtypeinfo('auto', '', 'autodetect type (default)') for typename in sorted(cftypes.keys()): printtypeinfo(typename, cftypes[typename].descinfo, cftypes[typename].description) sys.exit(err) stats=Stats() config=Config() cache=FileInfoCache() progress=TimedProgressMeter() def main(argv=None): if argv is None: argv = sys.argv[1:] manual=0 mode=0 typename='auto' try: optlist, args = getopt.getopt(argv, 'rRlLTCt:f:mMnNsSp:uUiIvVzZqQh?', ['list=', 'list0=', 'fixpaths=', 'strippaths=', 'showpaths=','renameformat=','progress=','unquote=','help','version', 'announceurl=','piece_size_pow2=','private_torrent','noprivate_torrent', #torrent options ]) except getopt.error, a: perror("cfv: %s"%a) printusage(1) try: if config.cmdlineglob=='y' or (config.cmdlineglob=='a' and os.name in ('os2', 'nt', 'dos')): from glob import glob globbed = [] for arg in args: if '*' in arg or '?' in arg or '[' in arg: g = glob(arg) if not g: raise CFVValueError, 'no matches for %s'%arg globbed.extend(g) else: globbed.append(arg) args = globbed if config.cmdlinesort: args.sort() prevopt='' for o,a in optlist: if o=='-T': mode=0 elif o=='-C': mode=1 elif o=='-t': if a=='help': printcftypehelp(err=0) if not a in ['auto']+cftypes.keys(): perror('cfv: type %s not recognized'%a) printcftypehelp(err=1) typename=a elif o=='-f': setup_output() manual=1 #filename selected manually, don't try to autodetect if mode==0: cache.set_testfiles(args) test(a, typename) else: if typename!='auto': make(cftypes[typename],a,args) else: testa='' if config.gzip>=0 and a[-3:]=='.gz': testa=a[:-3] cftype = auto_filename_match(a, testa) if not cftype: raise CFVValueError, 'specify a filetype with -t, or use standard extension' make(cftype,a,args) elif o=='-U': config.showunverified=0 elif o=='-u': if prevopt=='-u': config.showunverified=2 else: config.showunverified=1 elif o=='-I': config.ignorecase=0 elif o=='-i': config.ignorecase=1 elif o=='-n': config.rename=1 elif o=='-N': config.rename=0 elif o=='-s': config.search=1 elif o=='-S': config.search=0 elif o=='--renameformat': config.setx('renameformat', a) elif o=='-m': config.docrcchecks=0 elif o=='-M': config.docrcchecks=1 elif o=='-p': chdir(a) elif o=='-r': if prevopt=='-r': config.recursive=2 else: config.recursive=1 elif o=='-R': config.recursive=0 elif o=='-l': config.dereference=1 elif o=='-L': config.dereference=0 elif o=='-v': config.setx('verbose', 'v') elif o=='-V': if prevopt=='-V': config.setx('verbose', 'VV') else: config.setx('verbose', 'V') elif o=='-q': config.setx('verbose', 'q') elif o=='-Q': config.setx('verbose', 'Q') elif o=='--progress': config.setx('progress', a) elif o=='-z': if prevopt=='-z': config.gzip=2 else: config.gzip=1 elif o=='-Z': if prevopt=='-Z': config.gzip=-1 else: config.gzip=0 elif o=='--list' or o=='--list0': if a not in LISTARGS.keys(): raise CFVValueError, 'list arg must be one of '+`LISTARGS.keys()` config.list=LISTARGS[a] config.listsep = o=='--list0' and '\0' or '\n' if config.list==LISTUNVERIFIED: config.showunverified=1 set_stdout_special() #redirect all messages to stderr so only the list gets on stdout elif o=='--showpaths': config.setx('showpaths', a) elif o=='--strippaths': config.setx('strippaths', a) elif o=='--fixpaths': config.setx('fixpaths', a) elif o=='--unquote': config.setx('unquote', a) elif o=='--announceurl': config.setx('announceurl', a) elif o=='--piece_size_pow2': config.setx('piece_size_pow2', a) elif o=='--private_torrent': config.private_torrent=True elif o=='--noprivate_torrent': config.private_torrent=False elif o=='-h' or o=='-?' or o=='--help': printhelp() elif o=='--version': print 'cfv %s'%version try: if not nommap: print '+mmap' except NameError: pass try: print 'fchksum %s'%fchksum.version() except NameError: pass print 'python %08x-%s'%(sys.hexversion,sys.platform) sys.exit(0) prevopt=o except CFVValueError, e: perror('cfv: %s'%e) sys.exit(1) setup_output() if not manual: if mode==0: cache.set_testfiles(args) autotest(typename) else: if typename=='auto': typename=config.defaulttype make(cftypes[typename],None,args) if mode==0: show_unverified_files(args) #only print total stats if more than one checksum file has been checked. (or if none have) #We must also print stats here if there are unverified files or checksum file errors, since those conditions occur outside of the cf_stats section. if stats.subcount != 1 or stats.unverified or stats.cferror: stats.print_stats() sys.exit((stats.badcrc and 2) | (stats.badsize and 4) | (stats.notfound and 8) | (stats.ferror and 16) | (stats.unverified and 32) | (stats.cferror and 64)) if __name__=='__main__': main() cfv-1.18.3/test/0002755000175300017530000000000011212670127012414 5ustar donutdonutcfv-1.18.3/test/a/0002755000175300017530000000000011212670124012631 5ustar donutdonutcfv-1.18.3/test/a/C/0002755000175300017530000000000011212670124013013 5ustar donutdonutcfv-1.18.3/test/a/C/foo.bar0000644000175300017530000000000007255556517014275 0ustar donutdonutcfv-1.18.3/test/a/C/CcCc.md50000644000175300017530000000005207264311622014217 0ustar donutdonutd41d8cd98f00b204e9800998ecf8427e *foo.bar cfv-1.18.3/test/a/data10000644000175300017530000000001507255451114013550 0ustar donutdonutHello world! cfv-1.18.3/test/b/0002755000175300017530000000000011212670124012632 5ustar donutdonutcfv-1.18.3/test/b/data10000644000175300017530000000002007255451114013545 0ustar donutdonutHello world! hi cfv-1.18.3/test/foo/0002755000175300017530000000000011212670122013172 5ustar donutdonutcfv-1.18.3/test/foo/foo10000644000175300017530000000000607746062556014002 0ustar donutdonuthello cfv-1.18.3/test/foo/foo20000644000175300017530000000001107746062556013777 0ustar donutdonut13245678 cfv-1.18.3/test/foo/foo30000644000175300017530000000000007746062556013776 0ustar donutdonutcfv-1.18.3/test/foo/foo40000644000175300017530000000000707746062556014006 0ustar donutdonutbaraga cfv-1.18.3/test/foo/foo50000644000175300017530000000001307746062556014004 0ustar donutdonutbardeexexe cfv-1.18.3/test/foo/foo1.50000644000175300017530000000000007746062556014137 0ustar donutdonutcfv-1.18.3/test/foo/foo1.60000644000175300017530000000000007746062556014140 0ustar donutdonutcfv-1.18.3/test/testnoheadercrlf.sfv0000644000175300017530000000010007420062237016460 0ustar donutdonutdata1 B2A9E441 data2 B2A9E441 data3 841ADFA2 data4 FA323C6D cfv-1.18.3/test/corrupt/0002755000175300017530000000000011212670123014106 5ustar donutdonutcfv-1.18.3/test/corrupt/missingmain.par20000644000175300017530000000227407712635454017237 0ustar donutdonutPAR2PKTNX0pp>P!Ԑܸ7PAR 2.0FileDescHZQss(K]3?YV3q5TxYV3q5Tx data1PAR2PKTd =]>=Vq>O!Ԑܸ7PAR 2.0IFSCHZQss(K]3?|+4*JJ?8PAR2PKTk< vv 0!Ԑܸ7PAR 2.0FileDesc(`O6YV3q5TxYV3q5Tx data2PAR2PKTd|Zi7!Ԑܸ7PAR 2.0IFSC(`O6|+4*JJ?8PAR2PKT,V7.`o`!Ԑܸ7PAR 2.0FileDesc\ۗn@طnjTB-^dQ匙TB-^dQ data3PAR2PKTd`9xeRC!Ԑܸ7PAR 2.0IFSC\ۗn@طnj{Isk\@ڟdyPAR2PKT2Ģ01vhVM'd!Ԑܸ7PAR 2.0FileDesc<7K|D;E8yQyQHdata4PAR2PKT,yz l=6!!Ԑܸ7PAR 2.0IFSC<7K|D;E8 FzP!Ԑܸ7PAR 2.0FileDescHZQss(K]3?YV3q5TxYV3q5Tx data1PAR2PKTd =]>=Vq>O!Ԑܸ7PAR 2.0IFSCHZQss(K]3?|+4*JJ?8PAR2PKTk< vv 0!Ԑܸ7PAR 2.0FileDesc(`O6YV3q5TxYV3q5Tx data2PAR2PKTd|Zi7!Ԑܸ7PAR 2.0IFSC(`O6|+4*JJ?8PAR2PKT,V7.`o`!Ԑܸ7PAR 2.0FileDesc\ۗn@طnjTB-^dQ匙TB-^dQ data3PAR2PKTd`9xeRC!Ԑܸ7PAR 2.0IFSC\ۗn@طnj{Isk\@ڟdyPAR2PKT>zI#SDn!Ԑܸ7PAR 2.0MainHZQss(K]3?(`O6<7K|D;E8\ۗn@طnjPAR2PKTdpմ1+ ?!Ԑܸ7PAR 2.0CreatorCreated by par2cmdline version 0.1.cfv-1.18.3/test/testv09.p010000644000175300017530000000326007420024117014251 0ustar donutdonutPAR ._H#dÔA-=Eb+W`hHB YV3q5TxYV3q5Txdata1B YV3q5TxYV3q5Txdata2B TB-^dQ匙TB-^dQdata3BHyQyQdata4world! Hello Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! cfv-1.18.3/test/testv09.par0000644000175300017530000000055007420024117014432 0ustar donutdonutPAR 8-굥|DA-=Eb+W`hB YV3q5TxYV3q5Txdata1B YV3q5TxYV3q5Txdata2B TB-^dQ匙TB-^dQdata3BHyQyQdata4cfv-1.18.3/test/testencoding.torrent0000644000175300017530000000130710065674570016533 0ustar donutdonutd8:announce3:foo13:creation datei1066375449e8:encoding8:utf-16be4:infod5:filesld6:lengthi13e4:pathl10:data1eed6:lengthi13e4:pathl10:data2eed6:lengthi13e4:pathl10:data3eed6:lengthi1352e4:pathl10:data4eee4:name8:test12:piece lengthi64e6:pieces440:o ϛņ C2 f$L-WOu+]97ѰM>u:Ѣms e;h,Rx|& ABM{*,ʚ]x6,<蜶L@|f^i #E߭*zxׇx;S*,ԑTaz 5_+Iw ?h:Nt;^(Zf>9g}]ytKB>CUj2+ ⍠YVW D_2f$L-WOu+]97ѰM>u:Ѣms e;h,Rx|& ABM{*,ʚ]x6,<蜶L@|f^i #E߭*zxׇ钟+E[@cī_ eecfv-1.18.3/test/testnosizenodimsnodesc.crc0000644000175300017530000000044507524724400017730 0ustar donutdonutGenerated by: cfv (v1.11.1-pre1.72) Generated at: Wed, 17 Jul 2002 21:09:46 Find it at: http://cfv.sourceforge.net/ Filename CRC-32 -------- -------- data1 B2A9E441 data2 B2A9E441 data3 841ADFA2 data4 FA323C6D -------- -------- Count of files: 4 Total of sizes: 1,391 cfv-1.18.3/test/teststrip-none.csv40000644000175300017530000000002507441121506016207 0ustar donutdonutdata1,13,B2A9E441,/, cfv-1.18.3/test/testcrcrlf.bsdmd50000644000175300017530000000030407514357430015673 0ustar donutdonutMD5 (data1) = 59ca0efa9f5633cb0371bbc0355478d8 MD5 (data2) = 59ca0efa9f5633cb0371bbc0355478d8 MD5 (data3) = 8c9954e3422d801b5e1a1b64c25106e5 MD5 (data4) = 96d879de0782e286d4031a8de9e351f2 cfv-1.18.3/test/data10000644000175300017530000000001507255450710013331 0ustar donutdonutHello world! cfv-1.18.3/test/data20000644000175300017530000000001507255450710013332 0ustar donutdonutHello world! cfv-1.18.3/test/data30000644000175300017530000000001507255450710013333 0ustar donutdonutworld! Hello cfv-1.18.3/test/data40000644000175300017530000000251007255450710013336 0ustar donutdonutHello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! cfv-1.18.3/test/testfix.csv40000644000175300017530000000031107255451114014701 0ustar donutdonutdata1,13,B2A9E441,\a, data1,13,B2A9E441,a\, data1,13,B2A9E441,\a\, data1,16,092FB09B,\b, data1,16,092FB09B,b\, data1,16,092FB09B,\b\, data1,16,092FB09B,/b, data1,16,092FB09B,b/, data1,16,092FB09B,/b/, cfv-1.18.3/test/testquotedcase.sfv0000644000175300017530000000022607357011756016203 0ustar donutdonut; Generated by foobarredSFV v25.36 on 2001-10-04 at 07:39.32 ; "A/c/fOo.bAr" 00000000 "daTA1" B2A9E441 dAtA2 B2A9E441 "data3" 841ADFA2 data4 FA323C6D cfv-1.18.3/test/testcase.csv0000644000175300017530000000014607255556517014765 0ustar donutdonutDaTa1,13,B2A9E441, dAtA2,13,B2A9E441, DATA3,13,841ADFA2, dATa4,1352,FA323C6D, A/c/fOo.BaR,0,00000000, cfv-1.18.3/test/testencoding2.torrent.foo0000644000175300017530000000124210065536000017356 0ustar donutdonutd8:announce3:foo13:creation datei1066375449e8:encoding5:utf-84:infod5:filesld6:lengthi13e4:pathl3:★eed6:lengthi13e4:pathl3:☆eed6:lengthi13e4:pathl3:☮eed6:lengthi1352e4:pathl3:☀eee4:name6:ばか12:piece lengthi64e6:pieces440:o ϛņ C2 f$L-WOu+]97ѰM>u:Ѣms e;h,Rx|& ABM{*,ʚ]x6,<蜶L@|f^i #E߭*zxׇx;S*,ԑTaz 5_+Iw ?h:Nt;^(Zf>9g}]ytKB>CUj2+ ⍠YVW D_2f$L-WOu+]97ѰM>u:Ѣms e;h,Rx|& ABM{*,ʚ]x6,<蜶L@|f^i #E߭*zxׇ钟+E[@cī_ eecfv-1.18.3/test/foo2err1/0002755000175300017530000000000011212670125014051 5ustar donutdonutcfv-1.18.3/test/foo2err1/foo10000644000175300017530000000000607746062556014656 0ustar donutdonuthello cfv-1.18.3/test/foo2err1/foo20000644000175300017530000000001107746062556014653 0ustar donutdonut132aa678 cfv-1.18.3/test/foo2err1/foo30000644000175300017530000000000007746062556014652 0ustar donutdonutcfv-1.18.3/test/foo2err1/foo40000644000175300017530000000000707746062556014662 0ustar donutdonutbaraga cfv-1.18.3/test/foo2err1/foo50000644000175300017530000000001307746062556014660 0ustar donutdonutbardeexexe cfv-1.18.3/test/foo2err1/foo1.50000644000175300017530000000000007746062556015013 0ustar donutdonutcfv-1.18.3/test/foo2err1/foo1.60000644000175300017530000000000007746062556015014 0ustar donutdonutcfv-1.18.3/test/testcrlf.csv20000644000175300017530000000005607420062237015043 0ustar donutdonutdata1,13, data2,13, data3,13, data4,1352, cfv-1.18.3/test/testcrlf.csv40000644000175300017530000000012607420062237015043 0ustar donutdonutdata1,13,B2A9E441,, data2,13,B2A9E441,, data3,13,841ADFA2,, data4,1352,FA323C6D,, cfv-1.18.3/test/testcrlf.sha10000644000175300017530000000030410156263605015020 0ustar donutdonut47a013e660d408619d894b20806b1d5086aab03b *data1 47a013e660d408619d894b20806b1d5086aab03b *data2 e6186f3c0c9ecc04539a4869be9d64e88c9c3a9f *data3 fe6924862117e475997671d7786a4ff1fece5fef *data4 cfv-1.18.3/test/test.bsdmd50000644000175300017530000000027407343323766014512 0ustar donutdonutMD5 (data1) = 59ca0efa9f5633cb0371bbc0355478d8 MD5 (data2) = 59ca0efa9f5633cb0371bbc0355478d8 MD5 (data3) = 8c9954e3422d801b5e1a1b64c25106e5 MD5 (data4) = 96d879de0782e286d4031a8de9e351f2 cfv-1.18.3/test/fifotest/0002755000175300017530000000000011212670123014233 5ustar donutdonutcfv-1.18.3/test/fifotest/fifo.sha10000644000175300017530000000006210174277207015744 0ustar donutdonut885de6000dd9815627d3f8525d9bf752d448851d *foo.bar cfv-1.18.3/test/fifotest/fifo.md50000644000175300017530000000005210174277207015574 0ustar donutdonutc7c55566dc7d55e81909c7bf436ca690 *foo.bar cfv-1.18.3/test/fifotest/fifo.bsdmd50000644000175300017530000000006110174277207016265 0ustar donutdonutMD5 (foo.bar) = c7c55566dc7d55e81909c7bf436ca690 cfv-1.18.3/test/fifotest/fifo.sfvmd50000644000175300017530000000013610174277207016316 0ustar donutdonut; Generated by cfv v1.18 on 2005-01-21 at 21:04.23 ; foo.bar c7c55566dc7d55e81909c7bf436ca690 cfv-1.18.3/test/foo.torrent0000644000175300017530000000073507746062556014643 0ustar donutdonutd8:announce3:foo13:creation datei1066447039e4:infod5:filesld6:lengthi6e4:pathl4:foo1eed6:lengthi0e4:pathl6:foo1.5eed6:lengthi0e4:pathl6:foo1.6eed6:lengthi9e4:pathl4:foo2eed6:lengthi0e4:pathl4:foo3eed6:lengthi7e4:pathl4:foo4eed6:lengthi11e4:pathl4:foo5eee4:name3:foo12:piece lengthi4e6:pieces180:ǯ_zvԪDǿq:L9%ر\]`amnD]Xf|Ej:gpu4WX`tK ) Auy<-j,l=t>aK9W4 ͂/gT.B7#eb'tx}<;InF͟2eecfv-1.18.3/test/testcrcrlf.crc0000644000175300017530000000075507524724400015272 0ustar donutdonutGenerated by: cfv (v1.11.1-pre1.72) Generated at: Wed, 17 Jul 2002 21:09:46 Find it at: http://cfv.sourceforge.net/ Filename Filesize W x H CRC-32 Description -------- ---------- ------- -------- data1 13 0 x 0 B2A9E441 data2 13 0 x 0 B2A9E441 data3 13 0 x 0 841ADFA2 data4 1,352 0 x 0 FA323C6D -------- ---------- ------- -------- Count of files: 4 Total of sizes: 1,391 cfv-1.18.3/test/testcrcrlf.csv0000644000175300017530000000012607514357430015312 0ustar donutdonutdata1,13,B2A9E441, data2,13,B2A9E441, data3,13,841ADFA2, data4,1352,FA323C6D, cfv-1.18.3/test/testcrcrlf.md50000644000175300017530000000025007514357430015202 0ustar donutdonut59ca0efa9f5633cb0371bbc0355478d8 *data1 59ca0efa9f5633cb0371bbc0355478d8 *data2 8c9954e3422d801b5e1a1b64c25106e5 *data3 96d879de0782e286d4031a8de9e351f2 *data4 cfv-1.18.3/test/testcrcrlf.sfv0000644000175300017530000000017607514357430015322 0ustar donutdonut; Generated by cfv v1.5.1 on 2000-10-17 at 03:58.28 ; data1 B2A9E441 data2 B2A9E441 data3 841ADFA2 data4 FA323C6D cfv-1.18.3/test/test.crc0000644000175300017530000000072107524724400014067 0ustar donutdonutGenerated by: cfv (v1.11.1-pre1.72) Generated at: Wed, 17 Jul 2002 21:09:46 Find it at: http://cfv.sourceforge.net/ Filename Filesize W x H CRC-32 Description -------- ---------- ------- -------- data1 13 0 x 0 B2A9E441 data2 13 0 x 0 B2A9E441 data3 13 0 x 0 841ADFA2 data4 1,352 0 x 0 FA323C6D -------- ---------- ------- -------- Count of files: 4 Total of sizes: 1,391 cfv-1.18.3/test/test.csv0000644000175300017530000000011607255450710014112 0ustar donutdonutdata1,13,B2A9E441, data2,13,B2A9E441, data3,13,841ADFA2, data4,1352,FA323C6D, cfv-1.18.3/test/test.p010000644000175300017530000000326007417341722013724 0ustar donutdonutPAR :Ȍxq`n}S2V>PISxEl`hHB YV3q5TxYV3q5Txdata1B YV3q5TxYV3q5Txdata2B TB-^dQ匙TB-^dQdata3BHyQyQdata4world! Hello Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! Hello world! cfv-1.18.3/test/test.md50000644000175300017530000000024007255450710014002 0ustar donutdonut59ca0efa9f5633cb0371bbc0355478d8 *data1 59ca0efa9f5633cb0371bbc0355478d8 *data2 8c9954e3422d801b5e1a1b64c25106e5 *data3 96d879de0782e286d4031a8de9e351f2 *data4 cfv-1.18.3/test/test.par0000644000175300017530000000055007417341722014105 0ustar donutdonutPAR R?fOŕ$߀2V>PISxEl`hB YV3q5TxYV3q5Txdata1B YV3q5TxYV3q5Txdata2B TB-^dQ匙TB-^dQdata3BHyQyQdata4cfv-1.18.3/test/test.sfv0000644000175300017530000000016207255450710014116 0ustar donutdonut; Generated by cfv v1.5.1 on 2000-10-17 at 03:58.28 ; data1 B2A9E441 data2 B2A9E441 data3 841ADFA2 data4 FA323C6D cfv-1.18.3/test/testcrlf.bsdmd50000644000175300017530000000030007420062237015334 0ustar donutdonutMD5 (data1) = 59ca0efa9f5633cb0371bbc0355478d8 MD5 (data2) = 59ca0efa9f5633cb0371bbc0355478d8 MD5 (data3) = 8c9954e3422d801b5e1a1b64c25106e5 MD5 (data4) = 96d879de0782e286d4031a8de9e351f2 cfv-1.18.3/test/test.py0000755000175300017530000014263211027563505013763 0ustar donutdonut#! /usr/bin/env python # test.py - tests for cfv (Command-line File Verify) # Copyright (C) 2000-2005 Matthew Mueller # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA default_ns = globals().copy() import re,os,sys,string,operator,shutil,getopt,gzip,zlib,stat,traceback,time from glob import glob try: # tempfile.mkdtemp is only in python 2.3+ from tempfile import mkdtemp except ImportError: import tempfile def mkdtemp(): d = tempfile.mktemp() os.mkdir(d) return d try: #zip only in python2.0+ zip except NameError: def zip(a,b): return map(None, a, b) #not exactly the same, but good enough for us. try: try: import BitTorrent except ImportError: import BitTornado; BitTorrent = BitTornado except ImportError: BitTorrent = None fmt_info = { #name: (hascrc, hassize, cancreate, available) 'sha1': (1,0,1,1), 'md5': (1,0,1,1), 'bsdmd5': (1,0,1,1), 'sfv': (1,0,1,1), 'sfvmd5': (1,0,1,1), 'csv': (1,1,1,1), 'csv2': (0,1,1,1), 'csv4': (1,1,1,1), 'crc': (1,1,1,1), 'par': (1,1,0,1), 'par2': (1,1,0,1), 'torrent': (1,1,1,not not BitTorrent), } def fmt_hascrc(f): return fmt_info[f][0] def fmt_hassize(f): return fmt_info[f][1] def fmt_cancreate(f): return fmt_info[f][2] def fmt_available(f): return fmt_info[f][3] def allfmts(): return fmt_info.keys() if hasattr(operator,'gt'): op_gt=operator.gt op_eq=operator.eq else: def op_gt(a,b): return a>b def op_eq(a,b): return a==b class rcurry: def __init__(self, func, *args, **kw): self.curry_func = func self.curry_args = args self.curry_kw = kw def __call__(self, *_args, **_kwargs): kw = self.curry_kw.copy() kw.update(_kwargs) return apply(self.curry_func, (_args+self.curry_args), kw) class NullFile: def isatty(self): return 0 def write(self,s): pass def writelines(self,l): pass def flush(self): pass def close(self): pass nullfile = NullFile() def pathfind(p, path=string.split(os.environ.get('PATH',os.defpath),os.pathsep)): for d in path: if os.path.exists(os.path.join(d,p)): return 1 def pathjoin_and_mkdir(*components): """Join components of a filename together and create directory to contain the file, if needed.""" result = os.path.join(*components) path = os.path.split(result)[0] if not os.path.exists(path): os.makedirs(path) return result def readfile(fn): f = open(fn,'rb') d = f.read() f.close() return d def writefile_and_reopen(fn,data): """Write data to file, close, and then reopen readonly, and return the fd. This is for the benefit of windows, where you need to close and reopen the file as readonly in order for it to be openable simultaneously. """ f = open(fn,'wb') if data: f.write(data) f.close() f = open(fn,'rb') return f class stats: ok=0 failed=0 def logr(text): logfile.write(text); def log(text): logr(text+"\n"); def get_version_flags(): global ver_fchksum, ver_mmap s,o=runcfv(cfvcmd+" --version") ver_fchksum = string.find(o,'fchksum')>=0 ver_mmap = string.find(o,'mmap')>=0 def test_log_results(cmd,s,o,r,kw): """ cmd=command being tested (info only) s=return status o=output r=result (false=ok, anything else=fail (anything other than 1 will be printed)) """ log("*** testing "+cmd + (kw and ' '+str(kw) or '')); log(o); if r: stats.failed=stats.failed+1 print "failed test:",cmd result="FAILED"; if type(r)!=type(1) or r!=1: result=result+" (%s)"%r else: stats.ok=stats.ok+1 result="OK"; log("%s (%s)"%(result,s)); if r: log("\n".join(traceback.format_stack())) log(""); def expand_cmdline(cmd): argv = [] for arg in cmd.split(' '): #bad. shlex.split would be perfect, but its only in python >=2.3 arg = arg.replace('"','') # hack so --foo="bar" works. if '*' in arg or '?' in arg or '[' in arg: argv.extend(glob(arg)) else: argv.append(arg) return argv def runcfv_exe(cmd, stdin=None, stdout=None, stderr=None): try: import subprocess # subprocess module only in python >= 2.4, but it works on windows, unlike commands except ImportError: from commands import getstatusoutput runcmd = cfvenv+cfvexe+' '+cmd if stdin: runcmd = 'cat '+stdin+' | '+runcmd if stdout: runcmd = runcmd + ' > '+stdout if stderr: runcmd = runcmd + ' 2> '+stderr s,o = getstatusoutput(runcmd) if os.WIFSIGNALED(s): s = -os.WTERMSIG(s) else: s = os.WEXITSTATUS(s) return s,o else: def open_output(fn): if fn=='/dev/null' and not os.path.exists(fn): fn='nul' return open(fn,'wb') p_stdin = p_stdout = p_stderr = subprocess.PIPE if stdin: p_stdin = open(stdin,'rb') if stdout: p_stdout = open_output(stdout) else: p_stderr = subprocess.STDOUT if stderr: p_stderr = open_output(stderr) argv = [cfvexe]+expand_cmdline(cmd) proc = subprocess.Popen(argv, stdin=p_stdin, stdout=p_stdout, stderr=p_stderr) for f in p_stdin, p_stdout, p_stderr: if f not in (subprocess.PIPE, subprocess.STDOUT, None): f.close() obuf,ebuf = proc.communicate() if ebuf or obuf is None: assert not obuf o = ebuf else: o = obuf s = proc.returncode if o: if o[-2:] == '\r\n': o = o[:-2] elif o[-1:] in '\r\n': o = o[:-1] return s, o def runcfv_py(cmd, stdin=None, stdout=None, stderr=None): if stdin is not None and ver_fchksum: fileno = os.open(stdin, os.O_RDONLY | getattr(os,'O_BINARY', 0)) assert fileno >= 0 saved_stdin_fileno = os.dup(sys.stdin.fileno()) os.dup2(fileno, sys.stdin.fileno()) os.close(fileno) try: from cStringIO import StringIO StringIO().write(u'foo') # cStringIO with unicode doesn't work in python 1.6 except (ImportError, SystemError): from StringIO import StringIO obuf = StringIO() saved = sys.stdin,sys.stdout,sys.stderr,sys.argv cwd = os.getcwd() def open_output(file,obuf=obuf): if file: if file=="/dev/null": return nullfile return open(file,'wb') else: return obuf try: if stdin: sys.stdin = open(stdin,'rb') else: sys.stdin = StringIO() sys.stdout = open_output(stdout) sys.stderr = open_output(stderr) sys.argv = [cfvexe] + expand_cmdline(cmd) cfv_ns = default_ns.copy() try: exec cfv_compiled in cfv_ns s = 'no exit?' except SystemExit, e: s = e.code if stdin: sys.stdin.close() if stdout: sys.stdout.close() if stderr: sys.stderr.close() except KeyboardInterrupt: raise except: import traceback traceback.print_exc(file=obuf) s = 1 finally: sys.stdin,sys.stdout,sys.stderr,sys.argv = saved if locals().has_key('saved_stdin_fileno'): os.dup2(saved_stdin_fileno, sys.stdin.fileno()) os.close(saved_stdin_fileno) os.chdir(cwd) o = obuf.getvalue() if o: if o[-2:] == '\r\n': o = o[:-2] elif o[-1:] in '\r\n': o = o[:-1] return s, o def test_external(cmd,test): from commands import getstatusoutput s,o = getstatusoutput(cmd) r=test(s,o) test_log_results(cmd,s,o,r, None) def test_generic(cmd,test, **kw): #s,o=runcfv(cmd) s,o=apply(runcfv,(cmd,), kw) r=test(s,o) test_log_results(cfvenv+cfvexe+" "+cmd,s,o,r, kw) class cst_err(Exception): pass def cfv_stdin_test(cmd,file): s1=s2=None o1=o2='' r=0 try: s1,o1=runcfv(cmd+' '+file) if s1: raise cst_err, 2 s2,o2=runcfv(cmd+' -', stdin=file) if s2: raise cst_err, 3 x=re.search('^([^\r\n]*)'+re.escape(file)+'(.*)$[\r\n]{0,2}^-: (\d+) files, (\d+) OK. [\d.]+ seconds, [\d.]+K(/s)?$',o1,re.M|re.DOTALL) if not x: raise cst_err, 4 x2=re.search('^'+re.escape(x.group(1))+'[\t ]*'+re.escape(x.group(2))+'$[\r\n]{0,2}^-: (\d+) files, (\d+) OK. [\d.]+ seconds, [\d.]+K(/s)?$',o2,re.M) if not x2: raise cst_err, 5 except cst_err, er: r=er test_log_results('stdin/out of '+cmd+' with file '+file,(s1,s2),o1+'\n'+o2,r, None) def cfv_stdin_progress_test(t,file): s1=s2=None o1=o2=c1=c2='' r=0 dir = mkdtemp() try: try: cf1=os.path.join(dir,'cf1.'+t) cf2=os.path.join(dir,'cf2.'+t) s1,o1=runcfv("%s --progress=yes -C -t %s -f %s %s"%(cfvcmd,t,cf1,file)) if s1: raise cst_err, 2 s2,o2=runcfv("%s --progress=yes -C -t %s -f %s -"%(cfvcmd,t,cf2),stdin=file) if s2: raise cst_err, 3 if t!='csv2':#csv2 has only filesize, hence checksum never happens, so no progress x=re.match(re.escape(file)+r' : (\.{20}[-\b.#\\|/]*)[ \r\n]+'+re.escape(cf1)+': (\d+) files, (\d+) OK. [\d.]+ seconds, [\d.]+K(/s)?$',o1,re.M|re.DOTALL) if not x: raise cst_err, 4 x2=re.match(r' : (\.[-\b.#/|\\]*)[\t ]*[ \r\n]+'+re.escape(cf2)+': (\d+) files, (\d+) OK. [\d.]+ seconds, [\d.]+K(/s)?$',o2,re.M) if not x2: raise cst_err, 5 if t=='crc': c1 = readfile(cf1).replace(file,' '*len(file)) else: c1 = readfile(cf1).replace(file,'') c2 = readfile(cf2) if c1!=c2: raise cst_err, 6 except cst_err, er: r=er test_log_results('progress=yes stdin/out of '+t+' with file '+file,(s1,s2),o1+'\n'+o2+'\n--\n'+c1+'\n'+c2,r, None) finally: shutil.rmtree(dir) def rx_test(pat,str): if re.search(pat,str): return 0 return 1 def status_test(s,o,expected=0): if s==expected: return 0 return 1 rx_Begin=r'^(?:.* )?(\d+) files, (\d+) OK' rx_unv=r', (\d+) unverified' rx_notfound=r', (\d+) not found' rx_ferror=r', (\d+) file errors' rx_bad=r', (\d+) bad(crc|size)' rx_badcrc=r', (\d+) badcrc' rx_badsize=r', (\d+) badsize' rx_cferror=r', (\d+) chksum file errors' rx_misnamed=r', (\d+) misnamed' rx_End=r'(, \d+ differing cases)?(, \d+ quoted filenames)?. [\d.]+ seconds, [\d.]+K(/s)?$' rxo_TestingFrom=re.compile(r'^testing from .* \((.+?)\b.*\)[\n\r]*$', re.M) def optionalize(s): return '(?:%s)?'%s rx_StatusLine=rx_Begin+''.join(map(optionalize,[rx_badcrc,rx_badsize,rx_notfound,rx_ferror,rx_unv,rx_cferror,rx_misnamed]))+rx_End class OneOf: def __init__(self, *possibilities): self.possible = possibilities def __cmp__(self, a): if a in self.possible: return 0 return cmp(a,self.possible[0]) def __repr__(self): return 'OneOf'+repr(self.possible) def intize(s): return s and int(s) or 0 def icomp(foo): exp,act = foo if exp==-1: return 0 return exp!=act def tail(s): #the last line might not be what we want, since stdout and stderr can get mixed up in some cases. #return string.split(s,'\n')[-1] lines = s.splitlines() lines.reverse() for line in lines: if re.search(rx_StatusLine, line): return line return '' def cfv_test(s,o, op=op_gt, opval=0): x=re.search(rx_Begin+rx_End,tail(o)) if s==0 and x and x.group(1) == x.group(2) and op(int(x.group(1)),opval): return 0 return 1 def cfv_status_test(s,o, unv=0, notfound=0, badcrc=0, badsize=0, cferror=0, ferror=0): expected_status = (badcrc and 2) | (badsize and 4) | (notfound and 8) | (ferror and 16) | (unv and 32) | (cferror and 64) if s==expected_status: return 0 return 'bad status expected %s got %s'%(expected_status, s) def cfv_all_test(s,o, files=-2, ok=0, unv=0, notfound=0, badcrc=0, badsize=0, cferror=0, ferror=0, misnamed=0): sresult = cfv_status_test(s,o,unv=unv,notfound=notfound,badcrc=badcrc,badsize=badsize,cferror=cferror,ferror=ferror) if sresult: return sresult x=re.search(rx_StatusLine,tail(o)) if x: if files==-2: files = reduce(operator.add, [ok,badcrc,badsize,notfound,ferror]) expected = [files,ok,badcrc,badsize,notfound,ferror,unv,cferror,misnamed] actual = map(intize, x.groups()[:9]) if not filter(icomp, map(None,expected,actual)): return 0 return 'expected %s got %s'%(expected,actual) return 'status line not found in output' def cfv_unv_test(s,o,unv=1): x=re.search(rx_Begin+rx_unv+rx_End,tail(o)) if s!=0 and x and x.group(1) == x.group(2) and int(x.group(1))>0: if unv and int(x.group(3))!=unv: return 1 return 0 return 1 def cfv_unvonly_test(s,o,unv=1): x=re.search(rx_Begin+rx_unv+rx_End,tail(o)) if s!=0 and x and int(x.group(3))==unv: return 0 return 1 def cfv_notfound_test(s,o,unv=1): x=re.search(rx_Begin+rx_notfound+rx_End,tail(o)) if s!=0 and x and int(x.group(2))==0 and int(x.group(1))>0: if int(x.group(3))!=unv: return 1 return 0 return 1 def cfv_cferror_test(s,o,bad=1): x=re.search(rx_Begin+rx_cferror+rx_End,tail(o)) if s!=0 and x and int(x.group(3))>0: if bad>0 and int(x.group(3))!=bad: return 1 return 0 return 1 def cfv_bad_test(s,o,bad=-1): x=re.search(rx_Begin+rx_bad+rx_End,tail(o)) if s!=0 and x and int(x.group(1))>0 and int(x.group(3))>0: if bad>0 and int(x.group(3))!=bad: return 1 return 0 return 1 def cfv_typerestrict_test(s,o,t): matches = rxo_TestingFrom.findall(o) if not matches: return 1 for match in matches: if match != t: return 1 return 0 def cfv_listdata_test(s,o): if s==0 and re.search('^data1\0data2\0data3\0data4\0$',o,re.I): return 0 return 1 def joincurpath(f): return os.path.join(os.getcwd(), f) def cfv_listdata_abs_test(s,o): if s==0 and re.search('^'+re.escape('\0'.join(map(joincurpath, ['data1','data2','data3','data4'])))+'\0$',o,re.I): return 0 return 1 def cfv_listdata_unv_test(s,o): if s==32 and re.search('^test.py\0testfix.csv\0$',o,re.I): return 0 return 1 def cfv_listdata_bad_test(s,o): if s&6 and not s&~6 and re.search('^(d2.)?test4.foo\0test.ext.end\0test2.foo\0test3\0$',o,re.I): return 0 return 1 def cfv_version_test(s,o): x=re.search(r'cfv v([\d.]+) -',o) x2=re.search(r'cfv ([\d.]+) ',open(os.path.join(os.pardir,"README")).readline()) x3=re.search(r' v([\d.]+):',open(os.path.join(os.pardir,"Changelog")).readline()) if x: log('cfv: '+x.group(1)) if x2: log('README: '+x2.group(1)) if x3: log('Changelog: '+x3.group(1)) #if os.path.isdir(os.path.join(os.pardir,'debian')): # x4=re.search(r'cfv \(([\d.]+)-\d+\) ',open(os.path.join(os.pardir,"debian","changelog")).readline()) # if x4: log('deb changelog: '+x4.group(1)) # if not x or not x4 or x4.group(1)!=x.group(1): # return 1 if x and x2 and x3 and x.group(1)==x2.group(1) and x.group(1)==x3.group(1): return 0 return 1 def cfv_cftypehelp_test(s,o,expected): if s!=expected: return 1 for tname in allfmts()+['auto']: if o.count(tname)<1: return 'type %s not found in output'%tname return 0 def cfv_nooutput_test(s,o,expected=0): if s!=expected: return 1 if o: return 'output: %s'%(repr(o),) return 0 def T_test(f, extra=None): cmd=cfvcmd if extra: cmd=cmd+" "+extra test_generic(cmd+" -T -f test"+f,cfv_test) test_generic(cmd+" -i -T -f test"+f,cfv_test) #all tests should work with -i test_generic(cmd+" -m -T -f test"+f,cfv_test) #all tests should work with -m test_generic(cmd+" -T --list0=ok -f test"+f, cfv_listdata_test, stderr="/dev/null") test_generic(cmd+" -T --showpaths=n-r --list0=ok -f test"+f, cfv_listdata_test, stderr="/dev/null") test_generic(cmd+" -T --showpaths=n-a --list0=ok -f test"+f, cfv_listdata_test, stderr="/dev/null") test_generic(cmd+" -T --showpaths=a-a --list0=ok -f test"+f, cfv_listdata_test, stderr="/dev/null") test_generic(cmd+" -T --showpaths=2-a --list0=ok -f test"+f, cfv_listdata_test, stderr="/dev/null") test_generic(cmd+" -T --showpaths=y-r --list0=ok -f test"+f, cfv_listdata_test, stderr="/dev/null") test_generic(cmd+" -T --showpaths=y-a --list0=ok -f test"+f, cfv_listdata_abs_test, stderr="/dev/null") test_generic(cmd+" -T --showpaths=1-a --list0=ok -f test"+f, cfv_listdata_abs_test, stderr="/dev/null") #ensure all verbose stuff goes to stderr: test_generic(cmd+" -v -T --list0=ok -f test"+f, cfv_listdata_test, stderr="/dev/null") test_generic(cmd+" -v -T --list0=unverified -f test"+f+" test.py testfix.csv data1", cfv_listdata_unv_test, stderr="/dev/null") #test progress stuff. def progress_test(s,o): if cfv_test(s,o): return 1 if o.find('.'*10)<0: return 2 return 0 def noprogress_test(s,o): if cfv_test(s,o): return 1 if o.find('.'*10)>=0: return 2 return 0 if f.endswith('.csv2'): #csv2 has only filesize, hence checksum never happens, so no progress test_generic(cmd+" -T --progress=yes -f test"+f, noprogress_test) else: #test handling of COLUMNS env var #TODO: should actually check that the value is being respected... os.environ["COLUMNS"]="40" try: test_generic(cmd+" -T --progress=yes -f test"+f, progress_test) os.environ["COLUMNS"]="foobar" test_generic(cmd+" -T --progress=yes -f test"+f, progress_test) finally: del os.environ["COLUMNS"] test_generic(cmd+" -T --progress=yes -f test"+f, progress_test) test_generic(cmd+" -T --progress=auto -f test"+f, noprogress_test) test_generic(cmd+" -T --progress=no -f test"+f, noprogress_test) def gzC_test(f,extra=None,verify=None,t=None,d=None): cmd=cfvcmd if not t: t=f f2='test.C.'+f+'.tmp.gz' f='test.C.'+f+'.gz' if extra: cmd=cmd+" "+extra try: test_generic("%s -q -C -t %s -zz -f - %s"%(cmd,t,d), status_test, stdout=f2) test_generic("%s -C -f %s %s"%(cmd,f,d),cfv_test) try: ifd1 = gzip.open(f) try: if1 = ifd1.read() finally: ifd1.close() except (IOError,zlib.error), e: if1 = '%s: %s'%(f, e) try: ifd2 = gzip.open(f2) try: if2 = ifd2.read() finally: ifd2.close() except (IOError,zlib.error), e: if2 = '%s: %s'%(f2, e) if t in ('sfv', 'sfvmd5'): commentre=re.compile("^; Generated by .* on .*$",re.M|re.I) if1 = commentre.sub('',if1,1) if2 = commentre.sub('',if2,1) elif t=='crc': commentre=re.compile("^Generated at: .*$",re.M|re.I) if1 = commentre.sub('',if1,1) if2 = commentre.sub('',if2,1) r = if1 != if2 if r: o = "FILE1 %s:\n%s\nFILE2 %s:\n%s\n"%(f,if1,f2,if2) else: o = '' test_log_results('zcompare %s %s'%(f,f2),r,o,r, None) test_generic("%s -T -f %s"%(cmd,f),cfv_test) test_generic("%s -zz -T -f -"%(cmd),cfv_test,stdin=f) if verify: verify(f) finally: if os.path.exists(f2): os.unlink(f2) if os.path.exists(f): os.unlink(f) def C_test(f,extra=None,verify=None,t=None,d='data?'): gzC_test(f,extra=extra,t=t,d=d) cmd=cfvcmd if not t: t=f cfv_stdin_test(cmd+" -t"+f+" -C -f-","data4") cfv_stdin_progress_test(f,'data4') f='test.C.'+f fgz=f+'.gz' try: if extra: cmd=cmd+" "+extra test_generic("%s -C -f %s %s"%(cmd,f,d),cfv_test) test_generic("%s -T -f %s"%(cmd,f),cfv_test) test_generic("%s -T -f -"%(cmd),cfv_test,stdin=f) of = gzip.open(fgz,mode='wb') of.write(open(f,'rb').read()) of.close() test_generic("%s -zz -t%s -T -f -"%(cmd,t), cfv_test, stdin=fgz) if verify: verify(f) finally: os.unlink(f) os.unlink(fgz) dir='Ce.test' try: os.mkdir(dir) test_generic("%s -p %s -C -f %s"%(cmd,dir,f),rcurry(cfv_test,op_eq,0)) finally: os.rmdir(dir) def C_funkynames_test(t): d = mkdtemp() d2 = mkdtemp() try: num = 0 for i in range(1,256): n = chr(i) if n in (os.sep, os.altsep, '\n', '\r'): continue if t == 'torrent' and n in ('/','\\'): continue # "ValueError: path \ disallowed for security reasons" if t == 'torrent' and n in ('~',): n = 'foo'+n #same if n == os.curdir: n = 'foo'+n # can't create a file of name '.', but 'foo.' is ok. if t in ('sfv','sfvmd5') and n==';': n = 'foo'+n # ';' is comment character in sfv files, filename cannot start with it. if t == 'crc' and n.isspace(): n = n + 'foo' # crc format can't handle trailing whitespace in filenames try: f = open(os.path.join(d,n),'wb') f.write(n) f.close() os.mkdir(os.path.join(d2,n)) f = open(os.path.join(d2,n,n),'wb') f.write(n) f.close() except EnvironmentError: continue # stupid filesystem doesn't allow the character we wanted, oh well. num = num + 1 cfn = os.path.join(d,'funky.'+t) test_generic(cfvcmd+" -v -C -p %s -t %s -f %s"%(d,t,cfn), rcurry(cfv_all_test,files=num,ok=num)) test_generic(cfvcmd+" -v -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,files=num,ok=num)) test_generic(cfvcmd+" -v -u -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,files=num,ok=num,unv=0)) dcfn = os.path.join(d2,'funkydeep.'+t) test_generic(cfvcmd+" -v -rr -C -p %s -t %s -f %s"%(d2,t,dcfn), rcurry(cfv_all_test,files=num,ok=num)) test_generic(cfvcmd+" -v -T -p %s -f %s"%(d2,dcfn), rcurry(cfv_all_test,files=num,ok=num)) test_generic(cfvcmd+" -v -u -T -p %s -f %s"%(d2,dcfn), rcurry(cfv_all_test,files=num,ok=num,unv=0)) finally: shutil.rmtree(d) shutil.rmtree(d2) def ren_test(f,extra=None,verify=None,t=None): join=os.path.join dir='n.test' dir2=join('n.test','d2') basecmd=cfvcmd+' -r -p '+dir if extra: basecmd=basecmd+" "+extra cmd=basecmd+' --renameformat="%(name)s-%(count)i%(ext)s"' try: os.mkdir(dir) os.mkdir(dir2) fls=[join(dir,'test.ext.end'), join(dir,'test2.foo'), join(dir,'test3'), join(dir2,'test4.foo')] flsf=[join(dir,'test.ext-%i.end'), join(dir,'test2-%i.foo'), join(dir,'test3-%i'), join(dir2,'test4-%i.foo')] flsf_1=[join(dir,'test.ext.end-%i'), join(dir,'test2.foo-%i'), join(dir2,'test4.foo-%i')] flsf_2=[join(dir,'test3-%i')] def flsw(t,fls=fls): for fl in fls: open(fl,'wb').write(t) def flscmp(t,n,fls=flsf): for fl in fls: fn= n!=None and fl%n or fl try: o = open(fn,'rb').read() r = o!=t except IOError, e: r = 1 o = str(e) test_log_results('cmp %s for %s'%(fn,t),r,o,r, None) flsw('hello') test_generic("%s -C -t %s"%(cmd,f),cfv_test) flsw('1') test_generic(basecmd+" --showpaths=0 -v -T --list0=bad",cfv_listdata_bad_test,stderr="/dev/null") test_generic(basecmd+" --showpaths=0 -q -T --list0=bad",cfv_listdata_bad_test) test_generic("%s -Tn"%(cmd),cfv_bad_test) flsw('11') test_generic("%s -Tn"%(cmd),cfv_bad_test) flsw('123') test_generic("%s -Tn"%(cmd),cfv_bad_test) flsw('63') test_generic(cmd+' --renameformat="%(fullname)s" -Tn',cfv_bad_test) #test for formats without count too flsw('hello') test_generic("%s -Tn"%(cmd),cfv_test) flscmp('1',0) flscmp('11',1) flscmp('123',2) flscmp('63',1,fls=flsf_1) flscmp('63',3,fls=flsf_2) flscmp('hello',None,fls=fls) finally: shutil.rmtree(dir) def search_test(t,test_nocrc=0,extra=None): cfn = os.path.join(os.getcwd(), 'test.'+t) hassize = fmt_hassize(t) if test_nocrc: hascrc = 0 cmd = cfvcmd+" -m" else: hascrc = fmt_hascrc(t) cmd = cfvcmd if extra: cmd = cmd + " " + extra if not hascrc and not hassize: # if using -m and type doesn't have size, make sure -s doesn't do anything silly d = mkdtemp() try: for n,n2 in zip(range(1,5),range(4,0,-1)): shutil.copyfile('data%s'%n, os.path.join(d,'fOoO%s'%n2)) test_generic(cmd+" -v -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,notfound=4)) test_generic(cmd+" -v -s -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,notfound=4)) test_generic(cmd+" -v -s -n -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,notfound=4)) test_generic(cmd+" -v -s -u -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,notfound=4,unv=4)) finally: shutil.rmtree(d) # then return, since all the following tests would be impossible. return d = mkdtemp() try: def dont_find_same_file_twice_test(s,o): if not (o.count('fOoO3')==1 and o.count('fOoO4')==1): return str((o.count('fOoO3'), o.count('fOoO4'))) return cfv_all_test(s,o,ok=4,misnamed=4) test_generic(cmd+" -v -s -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,notfound=4)) for n,n2 in zip(range(1,5),range(4,0,-1)): shutil.copyfile('data%s'%n, os.path.join(d,'fOoO%s'%n2)) test_generic(cmd+" -v -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,notfound=4)) test_generic(cmd+" -v -s -T -p %s -f %s"%(d,cfn), dont_find_same_file_twice_test) test_generic(cmd+" -v -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,notfound=4)) test_generic(cmd+" -v -n -s -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,ok=4,misnamed=4)) test_generic(cmd+" -v -u -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,ok=4)) finally: shutil.rmtree(d) #the following tests two things: # 1) that it will copy/link to a file that is already OK rather than just renaming it again # 2) that it doesn't use the old cached value of a file's checksum before it got renamed out of the way. d = mkdtemp() try: misnamed1=misnamed2=4 if hassize and hascrc: experrs={'badcrc':1,'badsize':2} elif hassize: experrs={'badsize':2, 'ok':1} misnamed1=3 misnamed2=OneOf(3,4) #this depends on what order os.listdir finds stuff. (could be 3 or 4) else:#if hascrc: experrs={'badcrc':3} test_generic(cmd+" -v -s -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,notfound=4)) for n,n2 in zip([1,3,4],[4,2,1]): shutil.copyfile('data%s'%n, os.path.join(d,'data%s'%n2)) test_generic(cmd+" -v -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,notfound=1,**experrs)) test_generic(cmd+" -v -s -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,ok=4,misnamed=misnamed1)) test_generic(cmd+" -v -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,notfound=1,**experrs)) test_generic(cmd+" -v -n -s -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,ok=4,misnamed=misnamed2)) test_generic(cmd+" -v -u -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,ok=4)) finally: shutil.rmtree(d) #test whether ferrors during searching are ignored if hasattr(os, 'symlink'): d = mkdtemp() try: for n,n2 in zip([4],[2]): shutil.copyfile('data%s'%n, os.path.join(d,'foo%s'%n2)) for n in string.lowercase: os.symlink('noexist', os.path.join(d,n)) test_generic(cmd+" -v -s -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,ok=1,misnamed=1,notfound=3)) test_generic(cmd+" -v -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,notfound=4)) test_generic(cmd+" -v -n -s -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,ok=1,misnamed=1,notfound=3)) test_generic(cmd+" -v -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,ok=1,notfound=3)) finally: shutil.rmtree(d) #test if an error while renaming a misnamed file is properly handled d = mkdtemp() ffoo = fdata4 = None try: ffoo=writefile_and_reopen(os.path.join(d,'foo'), open('data4','rb').read()) #note that we leave the file open. This is because windows allows renaming of files in a readonly dir, but doesn't allow renaming of open files. So if we do both the test will work on both nix and win. os.chmod(d,stat.S_IRUSR|stat.S_IXUSR) try: os.rename(os.path.join(d,'foo'),os.path.join(d,'foo2')) print 'rename of open file in read-only dir worked? skipping this test.' except EnvironmentError: # if the rename failed, then we're good to go for these tests.. test_generic(cmd+" -v -n -s -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,files=4,ok=1,misnamed=1,ferror=1,notfound=3)) os.chmod(d,stat.S_IRWXU) fdata4=writefile_and_reopen(os.path.join(d,'data4'),'') os.chmod(d,stat.S_IRUSR|stat.S_IXUSR) test_generic(cmd+" -v -n -s -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,files=4,ok=1,misnamed=1,ferror=2,notfound=3)) finally: os.chmod(d,stat.S_IRWXU) if ffoo: ffoo.close() if fdata4: fdata4.close() shutil.rmtree(d) #test if misnamed stuff and/or renaming stuff doesn't screw up the unverified file checking d = mkdtemp() try: shutil.copyfile('data4', os.path.join(d,'foo')) test_generic(cmd+" -v -uu -s -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,files=4,ok=1,misnamed=1,notfound=3,unv=0)) test_generic(cmd+" -v -uu -s -n -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,files=4,ok=1,misnamed=1,notfound=3,unv=0)) open(os.path.join(d,'data1'),'w').close() if hassize: experrs={'badsize':1} else: experrs={'badcrc':1} test_generic(cmd+" -v -uu -s -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,files=4,ok=1,misnamed=0,notfound=2,unv=0,**experrs)) test_generic(cmd+" -v -uu -s -n -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,files=4,ok=1,misnamed=0,notfound=2,unv=1,**experrs)) finally: shutil.rmtree(d) if fmt_cancreate(t): #test deep handling d = mkdtemp() try: dcfn = os.path.join(d,'deep.'+t) os.mkdir(os.path.join(d, "aOeU.AoEu")) os.mkdir(os.path.join(d, "aOeU.AoEu", "boO.FaRr")) shutil.copyfile('data1', os.path.join(d, "aOeU.AoEu", "boO.FaRr", "DaTa1")) test_generic(cmd+" -v -rr -C -p %s -t %s -f %s"%(d,t,dcfn), rcurry(cfv_all_test,files=1,ok=1)) os.rename(os.path.join(d, "aOeU.AoEu", "boO.FaRr", "DaTa1"), os.path.join(d, "aOeU.AoEu", "boO.FaRr", "Foo1")) shutil.copyfile('data4', os.path.join(d, "aOeU.AoEu", "boO.FaRr", "DaTa1")) test_generic(cmd+" -v -s -T -p %s -f %s"%(d,dcfn), rcurry(cfv_all_test,files=1,ok=1,misnamed=1)) shutil.rmtree(os.path.join(d, "aOeU.AoEu")) os.mkdir(os.path.join(d, "AoEu.aOeU")) os.mkdir(os.path.join(d, "AoEu.aOeU", "BOo.fArR")) shutil.copyfile('data4', os.path.join(d, "AoEu.aOeU", "BOo.fArR","dAtA1")) shutil.copyfile('data1', os.path.join(d, "AoEu.aOeU", "BOo.fArR","Foo1")) test_generic(cmd+" -i -v -s -T -p %s -f %s"%(d,dcfn), rcurry(cfv_all_test,files=1,ok=1,misnamed=1)) if hassize: experrs={'badsize':1} else: experrs={'badcrc':1} test_generic(cmd+" -i -v -T -p %s -f %s"%(d,dcfn), rcurry(cfv_all_test,files=1,ok=0,**experrs)) test_generic(cmd+" -i -v -s -n -T -p %s -f %s"%(d,dcfn), rcurry(cfv_all_test,files=1,ok=1,misnamed=1)) test_generic(cmd+" -i -v -T -p %s -f %s"%(d,dcfn), rcurry(cfv_all_test,files=1,ok=1)) finally: shutil.rmtree(d) if fmt_cancreate(t) and hassize: d = mkdtemp() try: dcfn = os.path.join(d,'foo.'+t) os.mkdir(os.path.join(d, "aoeu")) dirsize = os.path.getsize(os.path.join(d, "aoeu")) f=open(os.path.join(d,"idth"),'wb'); f.write('a'*dirsize); f.close() test_generic(cmd+" -v -C -p %s -t %s -f %s"%(d,t,dcfn), rcurry(cfv_all_test,files=1,ok=1)) os.remove(os.path.join(d,"idth")) os.rename(os.path.join(d,"aoeu"), os.path.join(d,'idth')) def dont_find_dir_test(s,o): if not o.count('idth')==1: return str((o.count('idth'),)) return cfv_all_test(s,o,ok=0,notfound=1) test_generic(cmd+" -v -m -T -p %s -f %s"%(d,dcfn), dont_find_dir_test) # test not finding non-file things in normal mode test_generic(cmd+" -v -m -s -T -p %s -f %s"%(d,dcfn), dont_find_dir_test) # test not finding non-file things in search mode finally: shutil.rmtree(d) def quoted_search_test(): d = mkdtemp() try: join = os.path.join f = open(join(d,'foo.sfv'),'w') f.write(r""""data1" B2A9E441 "/data4" FA323C6D "aa1/data1" B2A9E441 "c:/aa1/data4" FA323C6D "aa3/data3" 841ADFA2 "\aa3\data4" FA323C6D "c:\aa4\bb4\data1" B2A9E441 "aa4/bb4/data4" FA323C6D""") f.close() shutil.copyfile('data1', pathjoin_and_mkdir(d, "foo1")) shutil.copyfile('data4', pathjoin_and_mkdir(d, "foo4")) shutil.copyfile('data1', pathjoin_and_mkdir(d, "aa1", "foo1")) shutil.copyfile('data4', pathjoin_and_mkdir(d, "aa1", "foo4")) shutil.copyfile('data3', pathjoin_and_mkdir(d, "aa3", "foo3")) shutil.copyfile('data4', pathjoin_and_mkdir(d, "aa3", "foo4")) shutil.copyfile('data1', pathjoin_and_mkdir(d, "aa4","bb4", "foo1")) shutil.copyfile('data4', pathjoin_and_mkdir(d, "aa4","bb4", "foo4")) test_generic(cfvcmd+r" -v --unquote=yes --strippaths=0 --fixpaths \\/ -s -T -p "+d,rcurry(cfv_all_test,ok=8,misnamed=8)) finally: shutil.rmtree(d) def symlink_test(): dir='s.test' dir1='d1' dir2='d2' try: os.mkdir(dir) os.mkdir(os.path.join(dir, dir1)) os.mkdir(os.path.join(dir, dir2)) if hasattr(os, 'symlink'): os.symlink(os.path.join(os.pardir, dir2), os.path.join(dir, dir1, 'l2')) os.symlink(os.path.join(os.pardir, dir1), os.path.join(dir, dir2, 'l1')) test_generic(cfvcmd+" -l -r -p "+dir, rcurry(cfv_test,op_eq,0)) test_generic(cfvcmd+" -L -r -p "+dir, rcurry(cfv_test,op_eq,0)) test_generic(cfvcmd+" -l -r -C -p "+dir, rcurry(cfv_test,op_eq,0)) test_generic(cfvcmd+" -L -r -C -p "+dir, rcurry(cfv_test,op_eq,0)) open(os.path.join(dir,dir1,'foo'),'w').close() open(os.path.join(dir,dir2,'bar'),'w').close() def r_unv_test(s,o): if cfv_unvonly_test(s,o,2): return 1 if string.count(o,'not verified')!=1: return 1 return 0 test_generic(cfvcmd+" -l -r -u -p "+dir, r_unv_test) test_generic(cfvcmd+" -L -r -u -p "+dir, r_unv_test) test_generic(cfvcmd+" -l -u -p "+dir, r_unv_test) test_generic(cfvcmd+" -L -u -p "+dir, r_unv_test) def r_unv_verbose_test(s,o): if cfv_unvonly_test(s,o,2): return 1 if string.count(o,'not verified')!=2: return 1 return 0 test_generic(cfvcmd+" -l -uu -p "+dir, r_unv_verbose_test) test_generic(cfvcmd+" -L -uu -p "+dir, r_unv_verbose_test) test_generic(cfvcmd+" -l -r -uu -p "+dir, r_unv_verbose_test) test_generic(cfvcmd+" -L -r -uu -p "+dir, r_unv_verbose_test) finally: shutil.rmtree(dir) def deep_unverified_test(): dir='dunv.test' try: join = os.path.join os.mkdir(dir) a = 'a' a_C = join(a, 'C') B = 'B' B_ushallow = join(B,'ushallow') B_ushallow_d = join(B_ushallow, 'd') u = 'u' u_u2 = join(u, 'u2') e = 'e' e_es = join(e, 'es') e2 = 'e2' e2_e2s = join(e2, 'e2s') e2_e2u = join(e2, 'e2u') for d in a, a_C, B, B_ushallow, B_ushallow_d, u, u_u2, e, e_es, e2, e2_e2s, e2_e2u: os.mkdir(join(dir,d)) datafns = ('DATa1', 'UnV1', join(a,'dAta2'), join(a, 'Unv2'), join(a_C,'dATa4'), join(a_C,'unV4'), join(B,'daTA3'), join(B,'uNv3'), join(B_ushallow,'uNvs'), join(B_ushallow_d,'unvP'), join(B_ushallow_d,'datA5'), join(u,'uNVu'), join(u,'UnvY'), join(u_u2,'UNVX'), join(e2_e2s,'DaTaE'),join(e2_e2u,'unVe2'),) lower_datafns = map(string.lower, datafns) for fn in datafns: open(join(dir,fn),'w').close() f = open(join(dir,'deep.md5'),'w') f.write("""d41d8cd98f00b204e9800998ecf8427e *b/DaTa3 d41d8cd98f00b204e9800998ecf8427e *B/ushAllOw/D/daTa5 d41d8cd98f00b204e9800998ecf8427e *a/c/DatA4 d41d8cd98f00b204e9800998ecf8427e *A/dATA2 d41d8cd98f00b204e9800998ecf8427e *E2/e2S/DAtae d41d8cd98f00b204e9800998ecf8427e *daTA1""") f.close() def r_test(s,o): if cfv_test(s,o,op_eq,6): return 1 if string.count(o,'not verified')!=0: return 1 return 0 def r_unv_test(s,o): if cfv_unvonly_test(s,o,10): return 1 if string.count(o,'not verified')!=8: return 1 if string.find(o,os.path.join('e','*'))>=0: return 1 if string.find(o,os.path.join('e2','*'))>=0: return 1 return 0 def r_unv_verbose_test(s,o): if cfv_unvonly_test(s,o,10): return 1 if string.count(o,'not verified')!=10: return 1 if string.find(o,'*')>=0: return 1 return 0 test_generic(cfvcmd+" -i -U -p "+dir, r_test) test_generic(cfvcmd+" -i -u -p "+dir, r_unv_test) test_generic(cfvcmd+" -i -uu -p "+dir, r_unv_verbose_test) test_generic(cfvcmd+" -i -U -p "+dir+" "+' '.join(lower_datafns), r_test) test_generic(cfvcmd+" -i -u -p "+dir+" "+' '.join(lower_datafns), r_unv_verbose_test) test_generic(cfvcmd+" -i -uu -p "+dir+" "+' '.join(lower_datafns), r_unv_verbose_test) finally: shutil.rmtree(dir) def test_encoding2(): """Non-trivial (actual non-ascii characters) encoding test. These tests will probably always fail unless you use a unicode locale and python 2.3+.""" if not BitTorrent: return d = mkdtemp() d2 = mkdtemp() try: cfn = os.path.join(d,u'\u3070\u304B.torrent') shutil.copyfile('testencoding2.torrent.foo', cfn) datafns = [ ('data1',u'\u2605'), ('data2',u'\u2606'), ('data3',u'\u262E'), ('data4',u'\u2600'), ] fnerrs=fnok=0 for srcfn,destfn in datafns: try: shutil.copyfile(srcfn, os.path.join(d2,destfn)) except (EnvironmentError,UnicodeError): fnerrs=fnerrs+1 else: fnok=fnok+1 test_generic(cfvcmd+" -q -T -p "+d, rcurry(cfv_status_test,notfound=fnok,ferror=fnerrs)) test_generic(cfvcmd+" -v -T -p "+d, rcurry(cfv_all_test,ok=0,notfound=fnok,ferror=fnerrs)) bakad = os.path.join(d,u'\u3070\u304B') os.mkdir(bakad) for srcfn,destfn in datafns: try: shutil.copyfile(srcfn, os.path.join(bakad,destfn)) except (EnvironmentError,UnicodeError): pass test_generic(cfvcmd+" -q -m -T -p "+d, rcurry(cfv_status_test,ferror=fnerrs)) test_generic(cfvcmd+" -v -m -T -p "+d, rcurry(cfv_all_test,ok=fnok,ferror=fnerrs)) if not fnerrs: #if some of the files can't be found, checking of remaining files will fail due to missing pieces test_generic(cfvcmd+" -q -T -p "+d, rcurry(cfv_status_test)) test_generic(cfvcmd+" -v -T -p "+d, rcurry(cfv_all_test,ok=4)) except: import traceback test_log_results('test_encoding2','foobar',''.join(traceback.format_exception(*sys.exc_info())),'foobar',{}) #yuck. I really should switch this crap all to unittest ... #finally: shutil.rmtree(unicode(d2)) shutil.rmtree(unicode(d)) def largefile2GB_test(): # hope you have sparse file support ;) fn = os.path.join('bigfile2','bigfile') f = open(fn,'wb') try: f.write('hi') f.seek(2**30) f.write('foo') f.seek(2**31) f.write('bar') f.close() test_generic(cfvcmd+" -v -T -p %s"%('bigfile2'), rcurry(cfv_all_test,ok=6)) finally: os.unlink(fn) def largefile4GB_test(): # hope you have sparse file support ;) fn = os.path.join('bigfile','bigfile') f = open(fn,'wb') try: f.write('hi') f.seek(2**30) f.write('foo') f.seek(2**31) f.write('bar') f.seek(2**32) f.write('baz') f.close() test_generic(cfvcmd+" -v -T -p %s"%('bigfile'), rcurry(cfv_all_test,ok=10)) finally: os.unlink(fn) def manyfiles_test(t): try: max_open = os.sysconf('SC_OPEN_MAX') except (AttributeError, ValueError, OSError): max_open = 1024 if not run_long_tests and max_open > 4096: print 'max open files is big (%i)'%max_open, max_open = 4096 print 'clipping to %i. Use --full to try the real value'%max_open num = max_open + 1 d = mkdtemp() try: for i in range(0,num): n = '%04i'%i f = open(os.path.join(d, n), 'w') f.write(n) f.close() cfn = os.path.join(d,'manyfiles.'+t) test_generic(cfvcmd+" -C -p %s -t %s -f %s"%(d,t,cfn), rcurry(cfv_all_test,ok=num)) test_generic(cfvcmd+" -T -p %s -f %s"%(d,cfn), rcurry(cfv_all_test,ok=num)) finally: shutil.rmtree(d) def specialfile_test(cfpath): if run_internal and ver_fchksum: #current versions of fchksum don't release the GIL, so this deadlocks if doing internal testing and using fchksum. return try: import threading except ImportError: return d = mkdtemp() cfn = os.path.split(cfpath)[1] try: fpath = os.path.join(d,'foo.bar') try: os.mkfifo(fpath) except (AttributeError, EnvironmentError): return shutil.copyfile(cfpath, os.path.join(d, cfn)) def pusher(fpath): f=open(fpath,'wb') f.write('a'*0x4000) f.flush() time.sleep(0.1) f.write('b'*0x4000) f.flush() time.sleep(0.1) f.write('c'*0x4000) f.close() t=threading.Thread(target=pusher,args=(fpath,)) t.start() s,o=runcfv("%s --progress=yes -T -p %s -f %s"%(cfvcmd,d,cfn)) t.join() r=0 if s: r=1 elif o.count('#')>1: r='count(#) = %s'%(o.count('#')) elif o.count('..'): r=3 test_log_results('specialfile_test(%s)'%cfn,s,o,r,None) finally: shutil.rmtree(d) def private_torrent_test(): cmd=cfvcmd tmpd = mkdtemp() try: needle = '7:privatei1' f = os.path.join(tmpd, 'test.torrent') test_generic("%s -C -f %s data1"%(cmd,f),cfv_test) data = readfile(f) test_log_results('should not contain private flag', 0, repr(data), needle in data, None) f = os.path.join(tmpd, 'test2.torrent') test_generic("%s --private_torrent -C -f %s data1"%(cmd,f),cfv_test) data = readfile(f) test_log_results('should contain private flag', 0, repr(data), needle not in data, None) finally: shutil.rmtree(tmpd) cfvenv='' cfvexe=os.path.join(os.pardir,'cfv') run_internal = 1 run_long_tests = 0 def show_help_and_exit(err=None): if err: print 'error:',err print print 'usage: test.py [-i|-e] [--full] [cfv]' print ' -i run tests internally' print ' -e launch seperate cfv process for each test' print ' --long include tests that may use large amounts of CPU or disk' print ' --help show this help' print print 'default [cfv] is:', cfvexe print 'default run mode is:', run_internal and 'internal' or 'external' sys.exit(1) try: optlist, args = getopt.getopt(sys.argv[1:], 'ie',['long','help']) except getopt.error, e: show_help_and_exit(e) if len(args)>1: show_help_and_exit("too many arguments") for o,a in optlist: if o=='--help': show_help_and_exit() elif o=='--long': run_long_tests = 1 elif o=='-i': run_internal = 1 elif o=='-e': run_internal = 0 else: show_help_and_exit("bad opt %r"%o) if args: cfvexe=args[0] #set everything to default in case user has different in config file cfvcmd='-ZNVRMUI --unquote=no --fixpaths="" --strippaths=0 --showpaths=auto-relative --progress=no --announceurl=url --noprivate_torrent' if run_internal: runcfv = runcfv_py _cfv_code = open(cfvexe,'r').read().replace('\r\n','\n').replace('\r','\n') cfv_compiled = compile(_cfv_code,cfvexe,'exec') else: runcfv = runcfv_exe logfile=open("test.log","w") def all_tests(): stats.ok = stats.failed = 0 symlink_test() deep_unverified_test() ren_test('sha1') ren_test('md5') ren_test('md5',extra='-rr') ren_test('bsdmd5') ren_test('sfv') ren_test('sfvmd5') ren_test('csv') ren_test('csv2') ren_test('csv4') ren_test('crc') if BitTorrent: ren_test('torrent') for t in 'sha1', 'md5', 'bsdmd5', 'sfv', 'sfvmd5', 'csv', 'csv2', 'csv4', 'crc', 'par', 'par2': search_test(t) search_test(t,test_nocrc=1) if BitTorrent: search_test('torrent',test_nocrc=1) #search_test('torrent',test_nocrc=1,extra="--strip=1") quoted_search_test() T_test(".sha1") T_test(".md5") T_test(".md5.gz") T_test("comments.md5") T_test(".bsdmd5") #test par spec 1.0 files: T_test(".par") T_test(".p01") #test par spec 0.9 files: T_test("v09.par") T_test("v09.p01") T_test(".par2") T_test(".vol0+1.par2") T_test(".csv") T_test(".sfv") T_test("noheader.sfv") T_test(".sfvmd5") T_test(".csv2") T_test(".csv4") T_test(".crc") T_test("nosize.crc") T_test("nodims.crc") T_test("nosizenodimsnodesc.crc") T_test("crlf.sha1") T_test("crlf.md5") T_test("crlf.bsdmd5") T_test("crlf.csv") T_test("crlf.csv2") T_test("crlf.csv4") T_test("crlf.sfv") T_test("noheadercrlf.sfv") T_test("crlf.crc") T_test("crcrlf.sha1") T_test("crcrlf.md5") T_test("crcrlf.bsdmd5") T_test("crcrlf.csv") T_test("crcrlf.csv2") T_test("crcrlf.csv4") T_test("crcrlf.sfv") T_test("noheadercrcrlf.sfv") T_test("crcrlf.crc") if BitTorrent: for strip in (0,1): T_test(".torrent",extra='--strip=%s'%strip) T_test("smallpiece.torrent",extra='--strip=%s'%strip) T_test("encoding.torrent",extra='--strip=%s'%strip) test_encoding2() #test handling of directory args in recursive testmode. (Disabled since this isn't implemented, and I'm not sure if it should be. It would change the meaning of cfv *) #test_generic(cfvcmd+" -r a",cfv_test) #test_generic(cfvcmd+" -ri a",cfv_test) #test_generic(cfvcmd+" -ri A",cfv_test) #test_generic(cfvcmd+" -rm a",cfv_test) #test_generic(cfvcmd+" -rim a",cfv_test) #test_generic(cfvcmd+" -r a/C",cfv_test) #test_generic(cfvcmd+" -ri A/c",cfv_test) #test handling of testfile args in recursive testmode test_generic(cfvcmd+" -r -p a "+os.path.join("C","foo.bar"),cfv_test) test_generic(cfvcmd+" -ri -p a "+os.path.join("c","fOo.BaR"),cfv_test) test_generic(cfvcmd+" -r -u -p a "+os.path.join("C","foo.bar"),cfv_test) test_generic(cfvcmd+" -ri -u -p a "+os.path.join("c","fOo.BaR"),cfv_test) test_generic(cfvcmd+" --strippaths=0 -T -f teststrip0.csv4",cfv_test) test_generic(cfvcmd+" --strippaths=1 -T -f teststrip1.csv4",cfv_test) test_generic(cfvcmd+" --strippaths=2 -T -f teststrip2.csv4",cfv_test) test_generic(cfvcmd+" --strippaths=all -T -f teststrip-1.csv4",cfv_test) test_generic(cfvcmd+" --strippaths=none -T -f teststrip-none.csv4",cfv_notfound_test) test_generic(cfvcmd+r" --strippaths=0 --fixpaths \\/ -T -f testdrivestrip.md5",rcurry(cfv_all_test,ok=4)) test_generic(cfvcmd+r" --strippaths=0 --unquote=yes --fixpaths \\/ -T -f testdrivestripquoted.md5",rcurry(cfv_all_test,ok=4)) test_generic(cfvcmd+r" --strippaths=0 --unquote=yes --fixpaths \\/ -T -f testdrivestripquoted.md5 data1 data3 data4",rcurry(cfv_all_test,ok=3)) test_generic(cfvcmd+" -i -T -f testcase.csv",cfv_test) test_generic(cfvcmd+" -T --unquote=yes -f testquoted.sfv",cfv_test) test_generic(cfvcmd+" -i --unquote=yes -T -f testquotedcase.sfv",cfv_test) test_generic(cfvcmd+" -i --unquote=yes -T -f testquotedcase.sfv DaTa1 "+os.path.join('a','C','Foo.bar'),rcurry(cfv_all_test,ok=2)) test_generic(cfvcmd+" -i -T -f testquoted.csv4",cfv_test) test_generic(cfvcmd+r" --fixpaths \\/ -T -f testfix.csv",cfv_test) test_generic(cfvcmd+r" --fixpaths \\/ -T -f testfix.csv4",cfv_test) test_generic(cfvcmd+r" -i --fixpaths \\/ -T -f testfix.csv4",cfv_test) C_test("bsdmd5","-t bsdmd5")#,verify=lambda f: test_generic("md5 -c "+f,status_test)) #bsd md5 seems to have no way to check, only create if pathfind('sha1sum'): #don't report pointless errors on systems that don't have sha1sum sha1verify=lambda f: test_external("sha1sum -c "+f,status_test) else: sha1verify=None C_test("sha1",verify=sha1verify) if pathfind('md5sum'): #don't report pointless errors on systems that don't have md5sum md5verify=lambda f: test_external("md5sum -c "+f,status_test) else: md5verify=None C_test("md5",verify=md5verify) C_test("csv") if pathfind('cksfv'): #don't report pointless errors on systems that don't have cksfv sfvverify=lambda f: test_external("cksfv -f "+f,status_test) else: sfvverify=None C_test("sfv",verify=sfvverify) C_test("sfvmd5","-t sfvmd5") C_test("csv2","-t csv2") C_test("csv4","-t csv4") C_test("crc") private_torrent_test() #test_generic("../cfv -V -T -f test.md5",cfv_test) #test_generic("../cfv -V -tcsv -T -f test.md5",cfv_test) for t in allfmts(): if fmt_cancreate(t) and fmt_available(t): C_funkynames_test(t) manyfiles_test(t) for fn in glob(os.path.join('fifotest','fifo.*')): specialfile_test(fn) test_generic(cfvcmd+" -m -v -T -t sfv", lambda s,o: cfv_typerestrict_test(s,o,'sfv')) test_generic(cfvcmd+" -m -v -T -t sfvmd5", lambda s,o: cfv_typerestrict_test(s,o,'sfvmd5')) test_generic(cfvcmd+" -m -v -T -t bsdmd5", lambda s,o: cfv_typerestrict_test(s,o,'bsdmd5')) test_generic(cfvcmd+" -m -v -T -t sha1", lambda s,o: cfv_typerestrict_test(s,o,'sha1')) test_generic(cfvcmd+" -m -v -T -t md5", lambda s,o: cfv_typerestrict_test(s,o,'md5')) test_generic(cfvcmd+" -m -v -T -t csv", lambda s,o: cfv_typerestrict_test(s,o,'csv')) test_generic(cfvcmd+" -m -v -T -t par", lambda s,o: cfv_typerestrict_test(s,o,'par')) test_generic(cfvcmd+" -m -v -T -t par2", lambda s,o: cfv_typerestrict_test(s,o,'par2')) test_generic(cfvcmd+" -u -t md5 -f test.md5 data* test.py test.md5",cfv_unv_test) test_generic(cfvcmd+" -u -f test.md5 data* test.py",cfv_unv_test) test_generic(cfvcmd+" -u -f test.md5 data* test.py test.md5",cfv_unv_test) test_generic(cfvcmd+r" -i -tcsv --fixpaths \\/ -Tu",lambda s,o: cfv_unv_test(s,o,None)) test_generic(cfvcmd+" -T -t md5 -f non_existant_file",cfv_cferror_test) test_generic(cfvcmd+" -T -f "+os.path.join("corrupt","missingfiledesc.par2"),cfv_cferror_test) test_generic(cfvcmd+" -T -f "+os.path.join("corrupt","missingmain.par2"),cfv_cferror_test) test_generic(cfvcmd+" -T -m -f "+os.path.join("corrupt","missingfiledesc.par2"),cfv_cferror_test) test_generic(cfvcmd+" -T -m -f "+os.path.join("corrupt","missingmain.par2"),cfv_cferror_test) if BitTorrent: test_generic(cfvcmd+" -T -f foo.torrent",cfv_test) test_generic(cfvcmd+" -T --strip=none -p foo -f ../foo.torrent",rcurry(cfv_all_test,notfound=7)) for strip in (0,1): test_generic(cfvcmd+" -T --strippaths=%s -p foo -f %s"%(strip,os.path.join(os.pardir,"foo.torrent")),rcurry(cfv_all_test,ok=7)) test_generic(cfvcmd+" -T --strippaths=%s -p foo2err -f %s"%(strip,os.path.join(os.pardir,"foo.torrent")), rcurry(cfv_all_test,ok=4,badcrc=3)) test_generic(cfvcmd+" -T --strippaths=%s -p foo2err -f %s foo1 foo4"%(strip,os.path.join(os.pardir,"foo.torrent")), rcurry(cfv_all_test,ok=0,badcrc=2)) test_generic(cfvcmd+" -T --strippaths=%s -p foo2err1 -f %s"%(strip,os.path.join(os.pardir,"foo.torrent")), rcurry(cfv_all_test,ok=6,badcrc=1)) test_generic(cfvcmd+" -T --strippaths=%s -p foo2err1 -f %s foo1 foo4"%(strip,os.path.join(os.pardir,"foo.torrent")), rcurry(cfv_all_test,ok=2)) test_generic(cfvcmd+" -T --strippaths=%s -p foo2badsize -f %s"%(strip,os.path.join(os.pardir,"foo.torrent")), rcurry(cfv_all_test,ok=5,badsize=1,badcrc=1)) test_generic(cfvcmd+" -T --strippaths=%s -p foo2badsize -f %s foo1 foo4"%(strip,os.path.join(os.pardir,"foo.torrent")), rcurry(cfv_all_test,ok=1,badcrc=1)) test_generic(cfvcmd+" -T --strippaths=%s -p foo2missing -f %s"%(strip,os.path.join(os.pardir,"foo.torrent")), rcurry(cfv_all_test,ok=4,badcrc=2,notfound=1)) test_generic(cfvcmd+" -T --strippaths=%s -p foo2missing -f %s foo1 foo4"%(strip,os.path.join(os.pardir,"foo.torrent")), rcurry(cfv_all_test,ok=0,badcrc=2)) d = mkdtemp() try: open(os.path.join(d,'foo'),'w').close() cmd = cfvcmd.replace(' --announceurl=url','') test_generic(cmd+" -C -p %s -f foo.torrent"%d,rcurry(cfv_all_test,files=1,cferror=1)) test_log_results("non-creation of empty torrent on missing announceurl?",'',repr(os.listdir(d)),len(os.listdir(d))>1,{}) finally: shutil.rmtree(d) if run_long_tests: largefile2GB_test() largefile4GB_test() test_generic(cfvcmd+" -t aoeu",rcurry(cfv_cftypehelp_test,1),stdout='/dev/null') test_generic(cfvcmd+" -t aoeu",rcurry(cfv_nooutput_test,1),stderr='/dev/null') test_generic(cfvcmd+" -t help",rcurry(cfv_cftypehelp_test,0),stderr='/dev/null') test_generic(cfvcmd+" -t help",rcurry(cfv_nooutput_test,0),stdout='/dev/null') test_generic(cfvcmd+" -h",cfv_nooutput_test,stdout='/dev/null') test_generic(cfvcmd+" -h",cfv_version_test,stderr='/dev/null') donestr="tests finished: ok: %i failed: %i"%(stats.ok,stats.failed) log("\n"+donestr) print donestr print 'testing...' get_version_flags() all_tests() if ver_fchksum: print 'testing without fchksum...' cfvenv="CFV_NOFCHKSUM=x "+cfvenv os.environ["CFV_NOFCHKSUM"]="x" get_version_flags() all_tests() if ver_mmap: print 'testing without mmap...' cfvenv="CFV_NOMMAP=x "+cfvenv os.environ["CFV_NOMMAP"]="x" get_version_flags() all_tests() cfv-1.18.3/test/test.md5.gz0000644000175300017530000000016607256225717014440 0ustar donutdonut!*:test.md5ʹ1\U8v/(׀7^Jq՘oY?3>hcfv-1.18.3/test/testquoted.csv40000644000175300017530000000024407577254644015441 0ustar donutdonut"foo.bar",0,00000000,"a/C", "data1",13,B2A9E441,a, data1,16,092FB09B,"b", "data1",13,B2A9E441,"", "data2",13,B2A9E441,, data3,13,841ADFA2,"", data4,1352,FA323C6D,, cfv-1.18.3/test/testsmallpiece.torrent0000644000175300017530000001601707746062556017076 0ustar donutdonutd8:announce3:foo13:creation datei1066375405e4:infod5:filesld6:lengthi13e4:pathl5:data1eed6:lengthi13e4:pathl5:data2eed6:lengthi13e4:pathl5:data3eed6:lengthi1352e4:pathl5:data4eee4:name4:test12:piece lengthi4e6:pieces6960:=4D(/F-a1%aR21YV1`5ȑ&pQ\B'Ho-c|R(>xS5R>Ub)܄Z{^:x}$sR21YV1`5ȑgg}0ίB.Ԑ񭌢!Wr.$X52}eXA&$+nj_B$s< (u+803a0OBgx4nյ&=4D(/F-a1%aR21YV1`5ȑ&pQ\B'Ho-c|R(>xS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FxS5R>Ub)܄ZP6t{[FB\PAR 2.0FileDescvfARB(ɣ?b 83Hd*5bigfilePAR2PKTj Ut;!:90^'EYL>B\PAR 2.0IFSCvfARBZ@}4&-#nL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(A)Hb$4"CgnU2B7nL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(A‰DtmZeJp,WnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(AnL~ .(Aإ06şquWm\zPAR2PKT\d9y_3:0^'EYL>B\PAR 2.0Main vfARBPAR2PKTdDZ{!s'H0^'EYL>B\PAR 2.0CreatorCreated by par2cmdline version 0.4.cfv-1.18.3/test/bigfile/bigfile.sha10000644000175300017530000000006210156263605016175 0ustar donutdonut9061ecaac4c349d7d8f321553c3182e388baf051 *bigfile cfv-1.18.3/test/bigfile/bigfile.crc0000644000175300017530000000053510123420524016102 0ustar donutdonutGenerated by: cfv (v1.17) Generated at: Sun, 19 Sep 2004 22:05:03 Find it at: http://cfv.sourceforge.net/ Filename Filesize W x H CRC-32 Description -------- ------------- ------- -------- bigfile 4,294,967,299 0 x 0 69880fc5 -------- ------------- ------- -------- Count of files: 1 Total of sizes: 4,294,967,299 cfv-1.18.3/test/bigfile/bigfile.csv0000644000175300017530000000003510123420524016121 0ustar donutdonutbigfile,4294967299,69880fc5, cfv-1.18.3/test/bigfile/bigfile.md50000644000175300017530000000005210123420524016012 0ustar donutdonutabe59e28c9a3f11b883f62c80a3833a5 *bigfile cfv-1.18.3/test/bigfile/bigfile.par0000644000175300017530000000024610123420524016114 0ustar donutdonutPAR #f+O$\$Q r=)`FF(ɣ?b 83Hd*5bigfilecfv-1.18.3/test/bigfile/bigfile.sfv0000644000175300017530000000002110123420524016117 0ustar donutdonutbigfile 69880FC5 cfv-1.18.3/test/bigfile/bigfile.sfvmd50000644000175300017530000000005110123420524016530 0ustar donutdonutbigfile abe59e28c9a3f11b883f62c80a3833a5 cfv-1.18.3/test/testnoheadercrcrlf.sfv0000644000175300017530000000010407514357430017017 0ustar donutdonutdata1 B2A9E441 data2 B2A9E441 data3 841ADFA2 data4 FA323C6D cfv-1.18.3/test/test.csv20000644000175300017530000000005207255450710014173 0ustar donutdonutdata1,13, data2,13, data3,13, data4,1352, cfv-1.18.3/test/test.csv40000644000175300017530000000012207420062237014170 0ustar donutdonutdata1,13,B2A9E441,, data2,13,B2A9E441,, data3,13,841ADFA2,, data4,1352,FA323C6D,, cfv-1.18.3/test/test.par20000644000175300017530000000251007663006277014173 0ustar donutdonutPAR2PKTNX0pp>P!Ԑܸ7PAR 2.0FileDescHZQss(K]3?YV3q5TxYV3q5Tx data1PAR2PKTd =]>=Vq>O!Ԑܸ7PAR 2.0IFSCHZQss(K]3?|+4*JJ?8PAR2PKTk< vv 0!Ԑܸ7PAR 2.0FileDesc(`O6YV3q5TxYV3q5Tx data2PAR2PKTd|Zi7!Ԑܸ7PAR 2.0IFSC(`O6|+4*JJ?8PAR2PKT,V7.`o`!Ԑܸ7PAR 2.0FileDesc\ۗn@طnjTB-^dQ匙TB-^dQ data3PAR2PKTd`9xeRC!Ԑܸ7PAR 2.0IFSC\ۗn@طnj{Isk\@ڟdyPAR2PKT2Ģ01vhVM'd!Ԑܸ7PAR 2.0FileDesc<7K|D;E8yQyQHdata4PAR2PKT,yz l=6!!Ԑܸ7PAR 2.0IFSC<7K|D;E8 FzzI#SDn!Ԑܸ7PAR 2.0MainHZQss(K]3?(`O6<7K|D;E8\ۗn@طnjPAR2PKTdpմ1+ ?!Ԑܸ7PAR 2.0CreatorCreated by par2cmdline version 0.1.cfv-1.18.3/test/test.sha10000644000175300017530000000030010156263605014145 0ustar donutdonut47a013e660d408619d894b20806b1d5086aab03b *data1 47a013e660d408619d894b20806b1d5086aab03b *data2 e6186f3c0c9ecc04539a4869be9d64e88c9c3a9f *data3 fe6924862117e475997671d7786a4ff1fece5fef *data4 cfv-1.18.3/test/test.torrent0000644000175300017530000000122707746062556015034 0ustar donutdonutd8:announce3:foo13:creation datei1066375449e4:infod5:filesld6:lengthi13e4:pathl5:data1eed6:lengthi13e4:pathl5:data2eed6:lengthi13e4:pathl5:data3eed6:lengthi1352e4:pathl5:data4eee4:name4:test12:piece lengthi64e6:pieces440:o ϛņ C2 f$L-WOu+]97ѰM>u:Ѣms e;h,Rx|& ABM{*,ʚ]x6,<蜶L@|f^i #E߭*zxׇx;S*,ԑTaz 5_+Iw ?h:Nt;^(Zf>9g}]ytKB>CUj2+ ⍠YVW D_2f$L-WOu+]97ѰM>u:Ѣms e;h,Rx|& ABM{*,ʚ]x6,<蜶L@|f^i #E߭*zxׇ钟+E[@cī_ eecfv-1.18.3/test/bigfile2/0002755000175300017530000000000011212670124014074 5ustar donutdonutcfv-1.18.3/test/bigfile2/bigfile2.par20000644000175300017530000011672011027563505016362 0ustar donutdonutPAR2PKT+ś US7^{쯩E*P(]PPAR 2.0FileDescB`Fj]ێQ!]-<0 uKHd*5bigfilePAR2PKT3ADal wo7^{쯩E*P(]PPAR 2.0IFSCB`Fj]ێQkӒę^Quf8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3dĿݝx1xcʓsef8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3df8oj-RP3d|h#Cu)XTajFPAR2PKT\+b#1E5"0K7^{쯩E*P(]PPAR 2.0MainXcB`Fj]ێQPAR2PKTdW +a7^{쯩E*P(]PPAR 2.0CreatorCreated by par2cmdline version 0.4.cfv-1.18.3/test/bigfile2/bigfile2.sha10000644000175300017530000000006211027563505016341 0ustar donutdonutce423fd8c3988d198292bd5a4a494c0f33251ae6 *bigfile cfv-1.18.3/test/bigfile2/bigfile2.torrent0000644000175300017530000001222111027563505017202 0ustar donutdonutd8:announce3:foo13:creation datei1214174438e4:infod6:lengthi2147483651e4:name7:bigfile12:piece lengthi8388608e6:pieces5140:u8cyp) $b1 _`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ6ETQ/ >_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮ_`>ef ȼ̰DԮbͷ d,=@f Meecfv-1.18.3/test/bigfile2/bigfile2.md50000644000175300017530000000005211027563505016171 0ustar donutdonut0ee9a8ca2119195d2db73c300dfc754b *bigfile cfv-1.18.3/test/bigfile2/bigfile2.par0000644000175300017530000000024611027563505016273 0ustar donutdonutPAR \2F@M5" &1],ӥ`FF!]-<0 uKHd*5bigfilecfv-1.18.3/test/bigfile2/bigfile2.sfv0000644000175300017530000000011011027563505016275 0ustar donutdonut; Generated by cfv v1.18.1 on 2008-06-22 at 23:07.47 ; bigfile 35ac22be cfv-1.18.3/test/testnoheader.sfv0000644000175300017530000000007407420062237015623 0ustar donutdonutdata1 B2A9E441 data2 B2A9E441 data3 841ADFA2 data4 FA323C6D cfv-1.18.3/test/testquoted.sfv0000644000175300017530000000022607357011756015347 0ustar donutdonut; Generated by foobarredSFV v27.65 on 2001-10-04 at 07:39.32 ; "a/C/foo.bar" 00000000 "data1" B2A9E441 data2 B2A9E441 "data3" 841ADFA2 data4 FA323C6D cfv-1.18.3/test/foo2badsize/0002755000175300017530000000000011212670123014617 5ustar donutdonutcfv-1.18.3/test/foo2badsize/foo10000644000175300017530000000000607746062556015426 0ustar donutdonuthello cfv-1.18.3/test/foo2badsize/foo20000644000175300017530000000001007746062556015422 0ustar donutdonut1324567 cfv-1.18.3/test/foo2badsize/foo30000644000175300017530000000000007746062556015422 0ustar donutdonutcfv-1.18.3/test/foo2badsize/foo40000644000175300017530000000000707746062556015432 0ustar donutdonutbaraga cfv-1.18.3/test/foo2badsize/foo50000644000175300017530000000001307746062556015430 0ustar donutdonutbardeexexe cfv-1.18.3/test/foo2badsize/foo1.50000644000175300017530000000000007746062556015563 0ustar donutdonutcfv-1.18.3/test/foo2badsize/foo1.60000644000175300017530000000000007746062556015564 0ustar donutdonutcfv-1.18.3/test/test.vol0+1.par20000644000175300017530000000301407663006277015206 0ustar donutdonutPAR2PKTC]XxFHSp!Ԑܸ7PAR 2.0RecvSlic_AJ . .(`((g3d.(=fEBD"(g3d.(=fEBD"(g3d.(=fEBD"(g3d.(=fEBD"(g3d.(=. .(`_AJ 7. .(`_AJ 7. .(`_AJ 7. .(`_AJ 7. .(PAR2PKTNX0pp>P!Ԑܸ7PAR 2.0FileDescHZQss(K]3?YV3q5TxYV3q5Tx data1PAR2PKTd =]>=Vq>O!Ԑܸ7PAR 2.0IFSCHZQss(K]3?|+4*JJ?8PAR2PKTk< vv 0!Ԑܸ7PAR 2.0FileDesc(`O6YV3q5TxYV3q5Tx data2PAR2PKTd|Zi7!Ԑܸ7PAR 2.0IFSC(`O6|+4*JJ?8PAR2PKT,V7.`o`!Ԑܸ7PAR 2.0FileDesc\ۗn@طnjTB-^dQ匙TB-^dQ data3PAR2PKTd`9xeRC!Ԑܸ7PAR 2.0IFSC\ۗn@طnj{Isk\@ڟdyPAR2PKT2Ģ01vhVM'd!Ԑܸ7PAR 2.0FileDesc<7K|D;E8yQyQHdata4PAR2PKT,yz l=6!!Ԑܸ7PAR 2.0IFSC<7K|D;E8 FzzI#SDn!Ԑܸ7PAR 2.0MainHZQss(K]3?(`O6<7K|D;E8\ۗn@طnjPAR2PKTdpմ1+ ?!Ԑܸ7PAR 2.0CreatorCreated by par2cmdline version 0.1.cfv-1.18.3/test/testdrivestrip.md50000644000175300017530000000025210071174412016111 0ustar donutdonut59ca0efa9f5633cb0371bbc0355478d8 */data1 59ca0efa9f5633cb0371bbc0355478d8 *\data2 8c9954e3422d801b5e1a1b64c25106e5 *c:\/\/data3 96d879de0782e286d4031a8de9e351f2 *//data4 cfv-1.18.3/Makefile0000644000175300017530000000635110200641606013074 0ustar donutdonutPYTHON=python prefix=/usr/local exec_prefix=${prefix} #finds the site-packages dir that matches the selected prefix, or if none do, falls back to wherever it can find one.. pkgdir=`$(PYTHON) -c 'import sys,re; x=filter(lambda x: re.match("$(prefix).*site-packages",x),sys.path); y=filter(lambda y: re.search("site-packages",y),sys.path); x.sort(lambda x,y: cmp(len(x),len(y))); y.sort(lambda x,y: cmp(len(x),len(y))); x.extend(y); print x[0]'` #nice little expression, huh? ;) bindir=${exec_prefix}/bin mandir=${prefix}/man install=/usr/bin/install -c install_dir=${install} -d install_data=${install} -m 0644 install_script=${install} -m 0755 foo: @echo 'to install cfv, type make install or install-wrapper.' @echo "manpage will be installed to: $(mandir)/man1" @echo "" @echo '"make install" will install like a standard script in' @echo "$(bindir)" @echo "" @echo '"make install-wrapper" will install a byte-compiled version in' @echo "$(pkgdir)" @echo 'with a small wrapper script in $(bindir)' @echo 'this allows for faster loading time since python does not need' @echo 'to parse the entire script every load.' @echo "" @echo 'You may edit the Makefile if you want to install somewhere else.' @echo "" @echo "Note that this method does not change how fast cfv actually runs," @echo "merely the time it takes from when you hit enter till it actually" @echo "starts doing something. For processing lots of files, this amount" @echo "of time will be inconsequential." #this will create a wrapper script that calls python directly (if we can find it), or using the bin/env trick. #we don't need to check for PYTHON being set to something, since os.path.join handles the case of the component being an absolute path cfv.wrapper: $(PYTHON) -c 'import string,os; py=filter(lambda x: os.path.isfile(x),map(lambda x: os.path.join(x,"$(PYTHON)"),string.split(os.environ["PATH"],":"))); py.append(" /usr/bin/env $(PYTHON)"); open("cfv.wrapper","w").write("#!%s\nimport cfv\ncfv.main()\n"%py[0])' $(DESTDIR)$(mandir)/man1 $(DESTDIR)$(bindir): $(install_dir) $@ install-wrapper-only: $(DESTDIR)$(bindir) cfv.wrapper install_man $(install_data) cfv $(DESTDIR)$(pkgdir)/cfv.py $(install_script) cfv.wrapper $(DESTDIR)$(bindir)/cfv install-wrapper: install-wrapper-only $(PYTHON) -c "import py_compile; py_compile.compile('$(DESTDIR)$(pkgdir)/cfv.py')" $(PYTHON) -O -c "import py_compile; py_compile.compile('$(DESTDIR)$(pkgdir)/cfv.py')" install: $(DESTDIR)$(bindir) install_man $(install_script) cfv $(DESTDIR)$(bindir)/cfv install_man: $(DESTDIR)$(mandir)/man1 $(install_data) cfv.1 $(DESTDIR)$(mandir)/man1/cfv.1 clean: -rm *.py[co] cfv.wrapper distclean: clean -rm -r cfv.nsi tags test/test.log `find . -regex '.*~\|.*/\.#.*' -o -name CVS -o -name .cvsignore` distclean-unixsrc: distclean -rm cfv.bat cfv.txt cfv.txt: %.txt: %.1 LANG=C man -l $< | sed -e 's/.//g' > $@ distclean-winsrc: distclean cfv.txt -rm Makefile cfv.1 mv cfv cfv.py todos *.txt COPYING README Changelog cfv.bat cfv.py test/*.py PY2EXEDIR=~/mnt/temp/cfv nsis-prepare: cfv.txt #hahaha, ugly hardcodedhackness cp cfv.txt cfv.nsi setup*.py $(PY2EXEDIR) cp Changelog $(PY2EXEDIR)/Changelog.txt cp COPYING $(PY2EXEDIR)/COPYING.txt cp cfv $(PY2EXEDIR)/cfv.py todos $(PY2EXEDIR)/*.txt cfv-1.18.3/cfv.10000644000175300017530000002740210200024672012272 0ustar donutdonut.TH cfv 1 "01 Feb 2005" .SH NAME cfv \- Verify file consistency with .sfv, .csv, .crc, .md5, md5sum, sha1sum, .torrent, par, or par2 files .SH SYNOPSIS .B cfv [\-p dir] [\-v|\-V|\-VV] [\-r|\-rr|\-R] [\-n|\-N] [\-\-renameformat ] [\-s|\-S] [\-zz|\-z|\-Z|\-ZZ] [\-T|\-C] [\-m|\-M] [\-i|\-I] [\-u|\-uu|\-U] [\-\-unquote ] [\-\-fixpaths ] [\-\-showpaths ] [\-\-list/\-\-list0 ] [\-\-announceurl ] [\-\-piece_size_pow2 ] [\-t type] [\-f file] [files...] .SH DESCRIPTION .B cfv verifies that the files you have are the same as those that the were used to create the checksum file. .SH OPTIONS .PP .IP "\-v" Enable printing of extra messages. .IP "\-V" Disable printing of extra messages. (default) .IP "\-VV" Like \-V, but don't print status line at end either. .IP "\-q" Like \-VV, but not even error messages are printed. Check the exit status. .IP "\-Q" Only status lines are printed, but not individual errors. .IP "\-\-progress VAL" Set when cfv should display progress bars. If no, progress bars are never displayed. If auto, progress bars are displayed when the output is to a tty.(default) If yes, progress is always displayed. .IP "\-r" Recursive mode 1. In create mode, make seperate chksum files for each dir. .IP "\-rr" Recursive mode 2. In create mode make a single file with deep listing in it. Both recursive modes are equivilant for test mode. .IP "\-R" Disable recursive mode (default) .IP "\-l" Follow directory symlinks in recursive mode. (default) .IP "\-L" Don't follow directory symlinks in recursive mode. .IP "\-T" Set test mode. (default) .IP "\-C" Set create mode. .IP "\-m" Check only for missing files (don't compare checksums) .IP "\-M" Check checksums (default) .IP "\-n" Rename bad files. With \-s, also renames misnamed files to the correct name. .IP "\-N" Don't rename bad files (default) .IP "\-\-renameformat string" Format string to use with \-n option. Simply, put any of the 4 strings %(fullname)s, %(name)s, %(ext)s, and %(count)i in the format string, along with whatever other text you wish. .br The default is '%(name)s.bad\-%(count)i%(ext)s' .br In detail, this is a standard python format string with mapping that contains {'fullname': original filename, 'name': fullname minus extension, 'ext': extension(including .), 'count': rename attempt}. If the format string does not contain count, then cfv will append '\-%(count)i' to the filename if the first attempt fails. .IP "\-s" Search for misnamed files. No effect in create mode. Also, keep in mind that using \-m together with \-s will do nothing if the checksum type doesn't include filesizes, and if it does, can give false positives if some files have the same size. .IP "\-S" Don't search for misnamed files. (default) .IP "\-i" Ignore case. Currently has no effect in create mode. .IP "\-I" Don't ignore case (default) .IP "\-u" Show unverified files. If no files in a directory are verified, shows dir/*. If no files in a directory or its subdirs are verified, shows dir/**. Has no effect in create mode. .IP "\-uu" Show each unverified file individually, no special directory handling. Has no effect in create mode. .IP "\-U" Don't show unverified files (default) .IP "\-zz" Force making/reading checksum files as gzipped files, even if not ending in .gz (mainly useful for "\-f \-") .IP "\-z" Make gzipped files in create mode. .IP "\-Z" Don't create gzipped files automatically. (default) .IP "\-ZZ" Never use gzip, even if file ends in ".gz". .IP "\-\-unquote BOOL" If yes, handle checksum files that were generated by buggy encoders that quote filenames in checksum formats that don't need it. Default is no, since quotes can be a valid character in a filename and we don't want to remove them if they are actually part of the filename. .IP "\-\-fixpaths string" Convert all occurances of any characters in string to the dir seperator for the current platform. No effect in create mode. Use an empty string to disable. .IP "\-\-strippaths VAL" Strip NUM leading components from file names in test mode. Similar to the \-p/\-\-strip options of patch. VAL may be 'none': leave exactly as is, 'all': strip everything but the filename, 0: strip the leading driveletter/slash (if any), 1+: strip this many path components in addition. The default is 0. .IP "\-\-showpaths VAL" Show paths in displayed filenames. VAL should be one of yes/1/no/0/auto/2 or absolute/relative, or one from first list and one from second joined by a \-. For backwards compatability, 1=yes, 0=none, 2=auto. The default is auto\-relative. Abrieviations are accepted. Examples: \-\-showpaths=y\-a always shows absolute paths. \-\-showpaths=n never shows paths. .IP "\-p dir" Change to directory before doing anything. .IP "\-f file" Specify the name of the checksum file to test or create. If file is \-, stdin (for \-T) or stdout (for \-C) will be used. .IP "\-t type" Specify the type of the file. Can be sfv, sfvmd5, csv, csv2, csv4, sha1, md5, bsdmd5, par, par2, torrent, crc, auto, or help. If the type is help, or an unknown type is given, a list of the types and their descriptions will be printed. The default is auto, which will detect the file type for you. When creating, if type is auto an sfv will be made, unless a different default has been set in the config file. .IP "\-\-list listset" Prints a raw listing of files in the given set (ok, bad, unverified, notfound). Usually used with \-q, but not strictly needed since specifying \-\-list will redirect all other messages to stderr. .IP "\-\-list0 listset" Like \-\-list but files are seperated by a null char. Useful in combination with xargs \-0. .IP "\-\-announceurl URL" Tracker announce URL for .torrent file creation. .IP "\-\-piece_size_pow2 N" Power of two to set piece size to for .torrent file creation. The default is 18, which gives a piece size of 2^18=256KB. .IP "\-h/\-\-help" Print help info. .IP "\-\-version" Print version of cfv and modules it uses. .P .B sfv is a Simple File Verify format file .br .B sfvmd5 is a Simple File Verify format file, using MD5 checksums rather than crc32. .br .B csv is a Comma Seperated Value file, with the fields being name,size,crc32, .br .B csv2 is a Comma Seperated Value file, with the fields being name,size, .br .B csv4 is a Comma Seperated Value file, with the fields being name,size,crc32,path .br .B sha1 is a sha1sum format file .br .B md5 is a md5sum format file .br .B bsdmd5 is a BSD md5 format file .br .B par is parchive v1 format file (test-only) .br .B par2 is parchive v2 format file (test-only) .br .B torrent is a BitTorrent metainfo file .br .B crc is a JPEG Sheriff format crc file .SH EXIT STATUS The exit status of cfv can be examined to determine what kind of errors, if any, occured. .P An exit status of 1 indictates a command line argument error, or an unhandled exception. .P Otherwise, the exit status will be a bitwise OR of: .IP 2 badcrc (a file had a different checksum than listed in the checksum file) .IP 4 badsize (a file had a different size than listed. Not all checksum file formats include file size) .IP 8 notfound (a file that was listed was not found) .IP 16 ferror (some other file error occured when trying to open/read a file) .IP 32 unverified (a file was not verified, only with \-u) .IP 64 cferror (a checksum file was not found or not recognized) .SH NOTES Since different platforms represent the path seperator differently, using recursive mode 2 (\-rr) is not recommended for anything other than personal usage. Although the addition of the fixpaths option can be used to work around this, it isn't guaranteed that whatever program others user have will have a similar feature. .P The \-s option is not currently implemented for .torrent files. (Unless you also use \-m) .SH EXAMPLES If no options are specified, the default will be \-T \-t auto, and it will search the current directory for any supported checksum files. .br .B cfv .P Force the file to test: .br .B cfv \-f funny.name .P Test only the files you have, (avoid file not found errors): .br .B cfv * .P Create a csv file for all the files in the current dir: .br .B cfv \-C \-tcsv .P Create a csv file for only the zip files in the current dir, and specify the filename: .br .B cfv \-C \-fsomezips.csv *.zip .P Check if all files in current and subdirs are verified, but don't verify checksums of files that are. (For example, before writing a directory to a cdr and you want to make sure all the files are verified.): .br .B cfv \-r \-m \-u .SH CONFIGURATION Upon startup, cfv will test for ~/.cfvrc and if it exists, read configuration information from it. The file consists of any number of lines, each having a single option name and the value seperated by a space. Empty lines and lines beginning with a # are ignored. .SH EXAMPLE CONFIGURATION #this is an example .cfvrc that specifies all the default options .br #don't be verbose (set to 1 or v for \-v, 0 or V for \-V, \-1 or VV for \-VV, \-2 or q for \-q, \-3 or Q for \-Q) .br verbose V .br #use progress meter when output is to a terminal (yes for always, no for never, auto for when output isatty) .br progress auto .br #create sfv files by default .br default sfv .br #sort dir listings before creating a checksum file .br dirsort 1 .br #sort command line specified files .br cmdlinesort 1 .br #expand wildcards in command line (yes for always, no for never, auto for when os.name is os2, nt, or dos) .br cmdlineglob auto .br #don't be recursive (set to 0 for \-R, 1 for \-r, 2 for \-rr) .br recursive 0 .br #follow symbolic links .br dereference 1 .br #don't show unverified files (set to 0 for \-U, 1 for \-u, 2 for \-uu) .br showunverified 0 .br #don't ignore case .br ignorecase 0 .br #don't use workaround for buggy encoders that quote filenames unnecessarily .br unquote 0 .br #don't fix any paths (note that there is a single space after fixpaths, thus the value it gets set to is an empty string) .br fixpaths .br #A more useful example would be: .br #fixpaths /\\ .br #don't strip leading directories (all to strip all path info, 0+ to strip the leading / and the first X components, none for nothing) .br strippaths 0 .br #show full paths in recursive mode (set to 0 for never, 1 for always, 2 for only in recursive mode) .br showpaths 2 .br #access checksum filenames that end with .gz as gzipped files (\-1 for never, 0 for with .gz, and 1 to make \-C make .gz files automatically) .br gzip 0 .br #don't rename bad files .br rename 0 .br #format to use for renaming bad files with \-n .br renameformat %(name)s.bad\-%(count)i%(ext)s .br #don't search for files .br search 0 .br #filename_type can be used to override what type of file to create when \-t isn't specified. .br #The format of the argument is =. Can be specified multiple times, the earlier instances having higher priority. .br #for example, the following line would cause cfv \-C \-f foo.md5 to create a 'sfvmd5' file rather than a 'md5' file. .br #filename_type sfvmd5=md5$ .P #torrent options: .br # you can specify a default announce url: .br #announceurl http://foo.bar/announce .br # piece size of 2^18 bytes (256KB): .br piece_size_pow2 18 .SH FILES .PP .IP "~/.cfvrc" cfv configuration file. See configuration section. .IP "~/_cfvrc" alternate configuration file name. (Since windows won't let you create files starting with a dot.) .SH ENVIRONMENT .PP .IP "HOME" Where to look for cfvrc file. Note that win9x doesn't set this to anything automatically. .IP "CFV_NOFCHKSUM" Set to a non-empty value to disable usage of python-fchksum module. .IP "CFV_NOMMAP" Set to a non-empty value to disable usage of mmap. .SH AUTHOR Matthew Mueller .P The latest version can be found at any of: .br http://cfv.sourceforge.net/ .br http://www.dakotacom.net/~donut/programs/cfv.html .br ftp://sunsite.unc.edu/pub/Linux/utils/file/ .P Other programs I have written can be found at: .br http://www.dakotacom.net/~donut/programs/ .SH "SEE ALSO" .BR md5sum (1), .BR sha1sum (1), .BR md5 (1), .BR xargs (1) cfv-1.18.3/README0000644000175300017530000000341311212657635012325 0ustar donutdonutcfv 1.18.3 Copyright 2000-2009 Matthew Mueller Requirements: Python >= 1.6 http://www.python.org/ (Python >= 2.3 is recommended for best unicode support) Optional: Python Imaging Library http://www.pythonware.com/products/pil/ (only needed if you want to create the dimensions column of .crc files) python-fchksum http://www.dakotacom.net/~donut/programs/fchksum.html (may speed up checksumming speed a bit, especially if your python wasn't built with the mmap module.) BitTorrent http://www.bitconjurer.org/BitTorrent/ (modules must be in the python search path to allow .torrent checking.) Unix Install: 1) run make with no arguments to see possible installation methods 2) run your chosen method (or manually put cfv and cfv.1 where you want) 3) read man page, cfv -h, etc. have fun. 4) optional: run tests to verify correct operation: cd test; ./test.py Windows Install: 1) move cfv.py to c:\python24\lib\site-packages\ 2) move cfv.bat to somewhere in the path 3) Edit the cfv.bat file if your python is installed somewhere other than c:\python24 (and obviously you'll need to change the path in step 1 too.) Copying: This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. See the file 'COPYING' for more information. I would appreciate it if you make modifications if you would send them to me for possible inclusion in the main source. You can get the latest version at http://cfv.sourceforge.net/ or http://www.dakotacom.net/~donut/programs/cfv.html or (ftp|http)://sunsite.unc.edu/pub/Linux/utils/file/ cfv-1.18.3/Changelog0000644000175300017530000002535411212657635013267 0ustar donutdonut2009-06-07 - v1.18.3: * Fix python2.6 md5 and sha module deprecation warnings. 2008-06-22 - v1.18.2: * Print help output to stdout under non-error conditions. (Requested by Simon Howard) * Work around mmap file descriptor leak in Python 2.4.2. * Support different module layout of BitTorrent 5.x. * Fix "struct integer overflow masking is deprecated" warning. (Reported by jjacobs2) * Add --private_torrent flag. (Requested by Anders Henke) * Work around bug in Python (only manifests with 64-bit Python version >= 2.5) which causes checksums of files > 4GB to be incorrectly calculated (when using mmap, eg, when the progress meter is disabled or not shown.) 2005-02-04 - v1.18.1: * Fix TypeError when using progress meter and checksumming stdin. * Fix progress meter spew when checksumming special files(devices, named pipes, etc.) * Sort list of checksum types in --help output. * Make -t help (or -t with unknown type) print list of types, with descriptions. 2005-01-11 - v1.18: * Added special hack for multi-file .torrents to automatically strip the first path component off if it doesn't exist. (Allows testing .torrent files within their dirs without having to manually do --strip=1) * Added .torrent creation support. * Fix testing torrents which contain large number of files giving "too many open files" errors. * Fix failure of -n in renaming bad files when testing a torrent file on windows due to windows not allowing renaming a file that is open. * Fix TypeError when using --progress=yes and not running under a tty and COLUMNS env var is set. (Reported by Steven Mueller) * Work around mmap error on large files. (Reported by Sapheriel) * Added sha1sum file support (-t sha1). 2004-07-10 - v1.17: * Handle .torrent files that have the creation date encoded as a string. * Handle .torrent files that specify the filename encoding. * Fix exceptions when trying to print unicode filenames containing non-ascii characters. * Add -s option to search for misnamed files. * Make .torrent support try BitTornado module if BitTorrent isn't found. * Fix --strippaths=0 handling of multiple leading slashes and of dos-style drive letters with forward slashes. * Improve handling of " and , in filenames in CSV files. (CSV parsing still won't be 100% correct, but doing any better would basically require adding a dependancy on some external csv module..) * Replace automagic buggy-quoting workaround with --unquote option, fixes --strippath handling of quoted filenames and avoids potential insanity with -n, -s, and quoted filenames. 2003-11-20 - v1.16: * Optimize progress meter display when checking lots of small files. * Add .torrent file support (requires BitTorrent.) 2003-08-23 - v1.15: * Add -l/-L options to toggle symlink following. * When following symlinks, don't die on cycles. * Ignore redundant FileDesc packets in PAR2 files. * Check that PAR2 files have FileDesc packets for every file listed in the Main packet. * Cache file checksums in memory in case they are verified by multiple checksum files. * -u now works correctly with deep checksum files. * -u option will now show dir/* if no files in a dir were verified. (--list=unverified still lists each unverified file.) * -uu can be used to force showing all unverified files. * Fix handling of the [files...] arguments in recursive test mode. * Add .p[0-9][0-9] (par1 recovery files) to the list of checksum file extensions to test automatically. * Fix FutureWarning "%X of negative int ..." on Python 2.3. * Add progress meter for file checksumming. (Can be toggled with the progress option.) * Only the new version 1.7 of fchksum module is supported. * Fix fixpaths option on windows. 2003-05-23 - v1.14: * Support md5sum files with comments in them. * Support PAR2 files. * Specifying -t in testmode without -f will cause cfv to only test files it finds if they are of the given type. * Doesn't try to test csv files with more than 4 fields as csv4 files. * Updated my email address. The old one should still forward for about a year, but you should switch to using the new one when contacting me. * Support _cfvrc as an alternate name for .cfvrc. * Add win32src zip and win32 installer exe (using NSIS and py2exe). 2002-12-18 - v1.13: * Fix exception in test mode when specifying -t and -f with a non-existant file. * Add support for sfvmd5 files generated by some windows software. (Just a SFV formatted file using md5 checksums rather than crc32.) * Handle quoted paths and filenames in csv4 files. * Add filename_type .cfvrc option to allow user overriding of what type of checksum file to create when -t isn't specified and -f is. * Only print the md5sum "tested in textmode" warning once, and print a count at the end. 2002-09-23 - v1.12: * Fix make install when target directories don't already exist. (Reported by Svend S. Sorensen) * Fix cfv -u -t blah -f foo.blah from reporting foo.blah as unverified. * Make create mode also display seperate stats per checksum file. * Make create mode be verbose in verbose mode. * Fix exception on error creating/writing a checksum file. (Eg, in a readonly dir, or a full drive.) * Accept files with weird line endings (eg \r\r\n). * Add support for (JPEG Sheriff format) .crc files. (The dimensions column is only added if The Python Imaging Library is avialable.) * Fix printing incorrect paths for subsequent files in recursive mode after encountering a directory that could not be accessed. 2002-04-08 - v1.11: * Due to architectural changes, unicode requirements(for .par support), etc, cfv now requires python 1.6 or higher. * Add test-only support for PAR files. (python 1.6 or 2.2 recommended for better performance.) * Print first SFV comment line in testing from blah.sfv line. (usually shows creating sfv program/creation time.) * Add --strippaths option. (Like patch's -p/--strip option.) * Revamp --showpaths option parsing, and add relative mode. (Note, auto-relative is now the default, to get the old behaviour specify "showpaths auto-absolute" in your .cfvrc) * Added cmdlineglob config option. * Added -Q command line arg to show only status lines. * Alternate method of setting verbose .cfvrc option (using command line verbosity option letter.) 2001-12-06 - v1.10: * Fix file.close() exception on -C in empty dir. * Add mmap support for faster checksumming when not using fchksum module. * Add CFV_NOFCHKSUM and CFV_NOMMAP env vars. (Mostly for testing/debugging.) * Handle files made by buggy programs that add quotes to filenames. * Print stats about number of case differences and quoted filenames. * In recursive mode, an unaccessible dir no longer causes an exception. (Reported by Harley Gorrell) * Fix excessive memory usage on deep recursive create mode in tree with lots of files. (Reported by Harley Gorrell) * Display seperate stats per checksum file, and (if needed) a total at the end. * In create mode, an already existing checksum file now causes a cferror, rather than an immediate exit. 2001-09-03 - v1.9: * Support bsd style md5 files (-t bsdmd5). * Make test.py not attempt to verify against md5sum if it is not available (ie, on bsd). (reported by Hannu Liljemark) * No longer attempt to set user/group in make install. * Add raw listing output options. (--list/--list0) 2001-05-17 - v1.8: * Fixed ignore case for deep (-rr) checksum files. * Added gzipped checksum file support. * Added change to directory first option (-p). * Makefile uses PYTHON var instead of hardcoded "python". * Added configurable renaming of bad files. * Extended --version output. 2001-02-27 - v1.7.2: * Fixed makefile not finding pkgdir if installing to /usr/local and no site-packages in /usr/local is in python's sys.path. * Added -q option. * Make exit status a bitwise or of the various possible error types. * Added cferror counter/exit status for unrecognized checksum files. * Catch errors opening checksum files and set cferror counter instead of exiting with unhandled exception. * Add error message for reading unrecognized lines in a checksum file, and set cferror counter. * Use Longs for per file sizes too, should be no large file issues anymore. (if you are using fchksum you should upgrade to 1.3 too.) * Time format uses .3 instead of .2 precision. 2001-02-22 - v1.7.1: * Fixed reading md5sum files that had dos eol on non-dos systems. * Loosened .sfv file recognition all the way. * Added --help alias for -h. * Added --version. 2000-11-19 - v1.7: * Made ignore case option always search for correct filename if using show unverified files mode. Fixes problem with incorrectly showing some files as unverified on FAT drives. * Caches dir entries for much faster ignore case operation. * Accepts - to represent stdin for data source and/or stdout for destination of checksums. * Added -VV option. * Errors printed to stderr instead of stdout. * Uses class instances instead of imp module for checksum types. * If using fchksum module, you now need at least version 1.2 (due to stdin ability). 2000-11-08 - v1.6: * Added ignore case option. * Added fix paths option. * Added handler for csv files with the dir seperate from the file name (-t csv4). * Makes any absolute paths found in checksum files into relative paths. * Added show paths option. 2000-10-17 - v1.5.2: * Added test suite. * Fixed 'x not in list' error when using -u and checksum file was not among the files to be tested. (First bug found with test suite ;) * Exits with non-zero status if any errors occured. 2000-10-12 - v1.5.1: * Fixed install wrapper stuff to compile _after_ it has been installed into the target dir. 2000-10-12 - v1.5: * Fixed "illegal argument type" error when creating csv format files. * Added note to manpage about -rr and path seperators. * Added show unverified files option. * Fixed md5 file testing when specifying files to test as command line args. 2000-10-05 - v1.4: * Added make install-wrapper option for faster startup times. * Uses long int value for total bytes read, to avoid overflow if you are checking a lot of files. (>=2GB, on 32bit archs) * Fixed divide by zero error on systems with lower resolution timers (ie, python-win32) * Ignore blank lines in .cfvrc * Loosened .sfv file recognition some more. * Handle error on creating/writing checksum file better. * Add handler for .csv files with no crc field (-t csv2). 2000-08-16 - v1.3: * .sfv file recognition loosened a bit (nvCRC32 says generated using instead of by) * uses fchksum module if available. (1.1x-2.0x faster) 2000-08-11 - v1.2: * recursive mode (-r and -rr) * only creates checksum file when there is actually something to put in it * if user doesn't specify files on command line, don't print errors about trying to add any directories and such 2000-07-10 - v1.1: * md5sum support (-t md5) * config file support * missing file only check (-m) 2000-06-22 - v1.0: first release cfv-1.18.3/COPYING0000644000175300017530000004311007255447576012513 0ustar donutdonut GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. cfv-1.18.3/setup-py2exe.py0000644000175300017530000000054411212657635014373 0ustar donutdonut# NOTE: This setup.py is ONLY for building the win32 .exe of cfv # For all other purposes, install using the Makefile from distutils.core import setup import py2exe setup(name="cfv", console=["cfv.py"], options={"py2exe": {"packages": ["encodings"]}}, version="1.18.3", author="Matthew Mueller", license="GPL", url="http://cfv.sourceforge.net", )