#!/usr/bin/python

from subprocess import call
from optparse import OptionParser
from random import uniform, seed
import sys, os, resource

def excluded(loc, *ranges) :
    for r in ranges :
        if loc >= r[0] and loc <= r[1] : return True
    return False

def valpos(fontlen, opts) :
    if opts.input :
        gen = []
        ifh = file(opts.input)
        for l in ifh.readlines() :
            f = l.split(',')
            gen.append((int(f[1], 0), int(f[2], 0)))
        ifh.close()
        def dogen() :
            for g in gen :
                yield (g)
        return dogen
    elif opts.position :
        if opts.value :
            def gen() :
                yield (int(opts.position, 0), int(opts.value, 0))
            return gen
        else :
            def gen() :
                while (1) :
                    yield(int(opts.position), int(uniform(0, 256)))
            return gen
    elif opts.value :
        def gen() :
            while (1) :
                yield(int(uniform(0, fontlen)), int(opts.value, 0))
        return gen
    elif opts.linear :
        def gen() :
            while (1) :
                for c in xrange(0, fontlen) :
                    yield (c, int(uniform(0, 256)))
        return gen
    else :
        def gen() :
            while (1) :
                yield(int(uniform(0, fontlen)), int(uniform(0, 256)))
        return gen

parser = OptionParser(usage="usage: %prog -f font [options] -- command")
parser.add_option("-l", "--logfile", help="Log results to this file")
parser.add_option("-f", "--font", help="Required font file to corrupt")
parser.add_option("-p", "--position", help="Specifies position to corrupt (hex)")
parser.add_option("-v", "--value", help="Specifies value to use (dec)")
parser.add_option("-i", "--input", help=".log file to read test values from")
parser.add_option("-V", "--verbose", action="store_true", help="Be noisy")
parser.add_option("-t", "--timeout", type="int", help="limit subprocess time in seconds")
parser.add_option("--memory", type="int", help="limit subprocess address space in MB")
parser.add_option("-c","--count",type='int',help='Flag every count iterations')
parser.add_option("-r","--random",help="Seed the random number generator with the given string")
parser.add_option("-L","--linear",action="store_true",help="Linearly process every location in the source font")

(opts, args) = parser.parse_args()

if opts.random : seed(opts.random)

fontlen = os.path.getsize(opts.font)
if opts.logfile :
    log = open(opts.logfile, "a")
else :
    log = sys.stdout

def rlimit() :
    if opts.timeout :
        resource.setrlimit(resource.RLIMIT_CPU, (opts.timeout, opts.timeout))
    if opts.memory :
        mem = opts.memory * 1024 * 1024
        resource.setrlimit(resource.RLIMIT_AS, (mem, mem))

font = None
count = 0
for loc, val in valpos(fontlen, opts)() :
    if font == None :
        font = open(opts.font, "r+b")
    font.seek(loc, 0)
    oldval = font.read(1)
    font.seek(loc, 0)
    while val == oldval : val = int(uniform(0, 256))
    font.write(chr(val))
    font.close()

    if opts.verbose :
        print "0x%X,%d" % (loc, val)
    count += 1
    if opts.count and count % opts.count == 0 :
        print count / opts.count,
        sys.stdout.flush()
    subfile = open("templog.txt", "w")
    retval = call(args, stdout=subfile, stderr=subfile, preexec_fn=rlimit, close_fds = True)
    if retval < 0 :
        print >>log, "%d,0x%x,%d" % (retval, loc, val)
    elif opts.verbose :
        print >>log, ",0x%x,%d" % (loc, val)
    log.flush()
    subfile.close()
    font = open(opts.font, "r+b")
    font.seek(loc, 0)
    font.write(oldval)

if font != None :
    font.close()

