2005-03-03 12:36:01 +00:00
|
|
|
#!/usr/bin/python
|
2005-03-02 14:28:41 +00:00
|
|
|
#
|
|
|
|
# Scyther caching mechanism
|
|
|
|
#
|
|
|
|
# Uses md5 hashes to store previously calculated results.
|
|
|
|
#
|
|
|
|
# (c)2005 Cas Cremers
|
|
|
|
#
|
|
|
|
#
|
2005-03-02 20:35:22 +00:00
|
|
|
# TODO:
|
|
|
|
#
|
|
|
|
# - Maybe it is an idea to time the output. If Scyther takes less
|
|
|
|
# than a second, we don't need to cache the output. That would
|
|
|
|
# reduce the required cache size significantly.
|
|
|
|
#
|
|
|
|
|
2005-03-02 14:28:41 +00:00
|
|
|
import md5
|
|
|
|
import commands
|
|
|
|
import os
|
2005-03-02 15:02:34 +00:00
|
|
|
import sys
|
|
|
|
from tempfile import NamedTemporaryFile, gettempdir
|
2005-03-02 14:28:41 +00:00
|
|
|
|
2005-03-03 12:36:01 +00:00
|
|
|
#----------------------------------------------------------------------------
|
|
|
|
# How to cal Scyther
|
|
|
|
#----------------------------------------------------------------------------
|
|
|
|
|
2005-03-02 14:28:41 +00:00
|
|
|
# scyther should reside in $PATH
|
|
|
|
def scythercall (argumentstring, inputfile):
|
|
|
|
clstring = "scyther " + argumentstring + " " + inputfile
|
|
|
|
(status,scout) = commands.getstatusoutput(clstring)
|
|
|
|
return (status,scout)
|
|
|
|
|
2005-03-03 12:36:01 +00:00
|
|
|
#----------------------------------------------------------------------------
|
|
|
|
# Cached evaluation
|
|
|
|
#----------------------------------------------------------------------------
|
|
|
|
|
2005-03-02 14:28:41 +00:00
|
|
|
# cached results
|
|
|
|
# input: a large string (consisting of read input files)
|
|
|
|
# argstring: a smaller string
|
2005-03-03 12:36:01 +00:00
|
|
|
def evaluate (argumentstring, inputstring):
|
2005-03-02 14:28:41 +00:00
|
|
|
|
|
|
|
def cacheid():
|
|
|
|
m = md5.new()
|
|
|
|
|
|
|
|
# # Determine scyther version
|
|
|
|
# (status, scout) = scythercall ("--version", "")
|
|
|
|
# if status == 1 or status < 0:
|
|
|
|
# # some problem
|
|
|
|
# print "Problem with determining scyther version!"
|
|
|
|
# os.exit()
|
|
|
|
# # Add version to hash
|
|
|
|
# m.update (scout)
|
|
|
|
|
|
|
|
# Add inputfile to hash
|
|
|
|
m.update (inputstring)
|
|
|
|
|
|
|
|
# Add arguments to hash
|
|
|
|
m.update (argumentstring)
|
|
|
|
|
|
|
|
# Return a readable ID (good for a filename)
|
|
|
|
return m.hexdigest()
|
|
|
|
|
2005-03-03 13:09:21 +00:00
|
|
|
# slashcutter
|
|
|
|
# Takes 'str': cuts of the first 'depth' strings of length
|
|
|
|
# 'width' and puts 'substr' in between
|
|
|
|
def slashcutter(str,substr,width,depth):
|
|
|
|
res = ""
|
|
|
|
while len(str)>width and depth>0:
|
|
|
|
res = res + str[0:width] + substr
|
|
|
|
str = str[width:]
|
|
|
|
depth = depth-1
|
|
|
|
return res + str
|
|
|
|
|
|
|
|
# Determine name
|
|
|
|
def cachefilename(id):
|
|
|
|
fn = gettempdir() + "/scyther/"
|
|
|
|
fn = fn + slashcutter(id,"/",2,4)
|
|
|
|
fn = fn + ".txt"
|
|
|
|
return fn
|
|
|
|
|
2005-03-02 15:02:34 +00:00
|
|
|
# Ensure directory
|
|
|
|
def ensureDirectory (path):
|
|
|
|
if not os.path.exists(path):
|
|
|
|
os.mkdir(path)
|
|
|
|
|
2005-03-02 14:28:41 +00:00
|
|
|
# Ensure directories for a file
|
|
|
|
def ensureDirectories (filename):
|
2005-03-02 15:02:34 +00:00
|
|
|
for i in range(1,len(filename)):
|
|
|
|
if filename[i] == '/':
|
|
|
|
np = i+1
|
|
|
|
ensureDirectory(filename[:np])
|
2005-03-02 14:28:41 +00:00
|
|
|
|
|
|
|
# Determine the unique filename for this test
|
2005-03-03 13:09:21 +00:00
|
|
|
cachefile = cachefilename(cacheid())
|
2005-03-02 14:28:41 +00:00
|
|
|
ensureDirectories(cachefile)
|
|
|
|
|
|
|
|
# Does it already exist?
|
|
|
|
if os.path.exists(cachefile):
|
|
|
|
# Great: return the cached results
|
2005-03-02 15:02:34 +00:00
|
|
|
f = open(cachefile,'r')
|
2005-03-02 14:28:41 +00:00
|
|
|
res = f.read()
|
|
|
|
f.close()
|
|
|
|
return (0,res)
|
|
|
|
else:
|
|
|
|
# Hmm, we need to compute this result
|
|
|
|
h = NamedTemporaryFile()
|
|
|
|
h.write(inputstring)
|
2005-03-02 15:02:34 +00:00
|
|
|
h.flush()
|
2005-03-02 14:28:41 +00:00
|
|
|
(status, scout) = scythercall (argumentstring, h.name)
|
|
|
|
h.close()
|
2005-03-02 15:02:34 +00:00
|
|
|
|
2005-03-02 15:09:29 +00:00
|
|
|
# Write cache file even if it's wrong
|
|
|
|
f = open(cachefile,'w')
|
|
|
|
f.write(scout)
|
|
|
|
f.close()
|
|
|
|
|
2005-03-02 14:28:41 +00:00
|
|
|
return (status,scout)
|
|
|
|
|
2005-03-03 12:36:01 +00:00
|
|
|
#----------------------------------------------------------------------------
|
|
|
|
# Parsing Output
|
|
|
|
#----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
# Parse output
|
|
|
|
def parse(scout):
|
|
|
|
results = {}
|
|
|
|
lines = scout.splitlines()
|
|
|
|
for line in lines:
|
|
|
|
data = line.split()
|
|
|
|
if len(data) > 6 and data[0] == 'claim':
|
|
|
|
claim = " ".join(data[1:4])
|
|
|
|
tag = data[6]
|
|
|
|
value = -1
|
|
|
|
if tag == 'failed:':
|
|
|
|
value = 0
|
|
|
|
if tag == 'correct:':
|
|
|
|
value = 1
|
|
|
|
if value == -1:
|
|
|
|
raise IOError, 'Scyther output for ' + commandline + ', line ' + line + ' cannot be parsed.'
|
|
|
|
results[claim] = value
|
|
|
|
return results
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------------
|
|
|
|
# Default tests
|
|
|
|
#----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
# Yield default protocol list (from any other one)
|
|
|
|
def default_protocols(plist):
|
2005-03-03 13:09:21 +00:00
|
|
|
plist.sort()
|
2005-03-03 12:36:01 +00:00
|
|
|
return ['../spdl/spdl-defaults.inc'] + plist
|
|
|
|
|
2005-03-03 13:09:21 +00:00
|
|
|
|
2005-03-03 12:36:01 +00:00
|
|
|
# Yield arguments, given a bound type:
|
|
|
|
# 0: fast
|
|
|
|
# 1: thorough
|
|
|
|
#
|
|
|
|
def default_arguments(plist,match,bounds):
|
|
|
|
n = len(plist)
|
|
|
|
# These bounds assume at least two protocols, otherwise
|
|
|
|
# stuff breaks.
|
|
|
|
if n < 2:
|
|
|
|
nmin = 2
|
|
|
|
else:
|
|
|
|
nmin = n
|
|
|
|
timer = 1
|
|
|
|
maxruns = 2
|
|
|
|
maxlength = 10
|
|
|
|
if bounds == 0:
|
|
|
|
timer = nmin**2
|
|
|
|
maxruns = 2*nmin
|
|
|
|
maxlength = 2 + maxruns * 3
|
|
|
|
elif bounds == 1:
|
|
|
|
timer = nmin**3
|
|
|
|
maxruns = 3*nmin
|
|
|
|
maxlength = 2 + maxruns * 4
|
|
|
|
else:
|
|
|
|
print "Don't know bounds method", bounds
|
|
|
|
sys.exit()
|
|
|
|
|
|
|
|
args = "--arachne --timer=%i --max-runs=%i --max-length=%i" % (timer, maxruns, maxlength)
|
|
|
|
matching = "--match=" + str(match)
|
|
|
|
allargs = "--summary " + matching + " " + args
|
|
|
|
return allargs
|
|
|
|
|
|
|
|
# Yield test results
|
|
|
|
def default_test(plist, match, bounds):
|
|
|
|
pl = default_protocols(plist)
|
|
|
|
args = default_arguments(plist,match,bounds)
|
|
|
|
|
|
|
|
input = ""
|
|
|
|
for fn in pl:
|
|
|
|
if len(fn) > 0:
|
|
|
|
f = open(fn, "r")
|
|
|
|
input = input + f.read()
|
|
|
|
f.close()
|
|
|
|
|
|
|
|
# Use Scyther
|
|
|
|
(status,scout) = evaluate(args,input)
|
|
|
|
return (status,scout)
|
|
|
|
|
|
|
|
# Test, check for status, yield parsed results
|
|
|
|
def default_parsed(plist, match, bounds):
|
|
|
|
(status,scout) = default_test(plist, match, bounds)
|
|
|
|
if status == 1 or status < 0:
|
|
|
|
# Something went wrong
|
|
|
|
print "*** Error when checking [", plist, match, bounds, "]"
|
|
|
|
print
|
|
|
|
sys.exit()
|
|
|
|
return parse(scout)
|
|
|
|
|
|
|
|
|
2005-03-02 14:28:41 +00:00
|
|
|
|