#!@PYTHON@ # -*- mode: python; coding: utf-8 -*- # Copyright (C) 2011, 2012 Laboratoire de Recherche et Développement # de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # # Spot 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. # # Spot 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 Spot; see the file COPYING. If not, write to the Free # Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA # 02111-1307, USA. import os import sys script = ('SCRIPT_NAME' in os.environ) if script: # 3600s = 1h sys.stdout.write("""Cache-Control: max-age=3600 Content-Type: text/html """) # Directory for temporary files (images and other auxiliary files). imgdir = 'spotimg' # Cache lookup for the QUERY_STRING qs = os.getenv('QUERY_STRING') if qs: import hashlib # We (optimistically) assume no collision from sha1(qs) cachedir = imgdir + '/' + hashlib.sha1(qs.encode('utf-8')).hexdigest() cachename = cachedir + '/html' try: # Is this a request we have already processed? cache = open(cachename, "rb", 0) if hasattr(sys.stdout, 'buffer'): # Python 3+ sys.stdout.buffer.write(cache.read()) else: # Python 2.x sys.stdout.write(cache.read()) # Touch the directory containing the files we used, so # it that it survives the browser's cache. os.utime(cachedir, None) exit(0) except IOError: # We failed to open the file. # Let's run the rest of the script to create it. pass elif script: sys.stdout.write("QUERY_STRING unset!\n") exit(0) # Location of the dot command dot = '@DOT@' dot_bgcolor = '-Gbgcolor=#FFFFFF00' svg_output = False # FIXME: SVG output seems to be working well with # Firefox only. We have to figure out how # to get the correct size and transparent # background in Chrome. if not script: # If this is not run as a cgi script, let's start an HTTP server. try: # Python 3+ from http.server import CGIHTTPRequestHandler, HTTPServer except ImportError: # Python 2.x from CGIHTTPServer import CGIHTTPRequestHandler from BaseHTTPServer import HTTPServer class MyHandler(CGIHTTPRequestHandler): def is_cgi(self): if self.path.startswith('/cgi-bin/spot.py'): self.cgi_info = '', self.path[9:] return True return False server_address=('',8000) if not os.access(imgdir, os.F_OK): # 493 = 0755 but we would have to write 0755 or 0o755 # depending on the python version... os.mkdir(imgdir, 493) sys.stdout.write("Directory spotimg/ created.\n") httpd = HTTPServer(server_address, MyHandler) sys.stdout.write("Point your browser to http://localhost:8000/ltl2tgba.html\n") httpd.serve_forever() import cgi import cgitb; cgitb.enable() import signal import time import os.path # We do not output in cachedir directely, in case two # CGI scripts process the same request concurrently. tmpdir = cachedir + '-' + str(os.getpid()) cachename = tmpdir + '/html' sys.stdout.flush() # Reopen stdout without buffering sys.stdout = os.fdopen(sys.stdout.fileno(), "wb", 0) # Redirect stderr to stdout at a low level (so that # even errors from subprocesses get printed). os.dup2(sys.stdout.fileno(), sys.stderr.fileno()) # Create the temporary cache directory os.mkdir(tmpdir, 493) # See comment above about 0o755 or 0755. # Redirect stdout to the cache file, at a low level # for similar reason. fd = os.open(cachename, os.O_CREAT | os.O_WRONLY, 420) # 420 = 0644 os.dup2(fd, sys.stdout.fileno()) # We had to reopen stdout in binary mode to enable unbuffered output, # (disallowed on text I/O by Python 3.x) so starting now, we are not # allowed to send strings to sys.stdout. Always use the following # method instead. def unbufprint(s): sys.stdout.write(s.encode("utf-8")) def finish(kill = False): # Output the result and exit. os.dup2(sys.stderr.fileno(), sys.stdout.fileno()) cache = open(cachename, "rb", 0) sys.stdout.write(cache.read()) # Rename tmpdir to its permanent name for caching purpose. # os.rename will fail if cachedir already exist. Since we tested # that initially, it can only happen when two CGI script are # processing the same request concurrently. In that case the # other result is as good as ours, so we just ignore the error. # (We don't bother removing the temporary directory -- it will be # removed by the next cache prune and cannot be created again in # the meantime.) try: os.rename(tmpdir, cachedir) except OSError: pass if kill: # Kill all children os.kill(0, signal.SIGTERM) # Should we prune the cache? stamp = imgdir + '/cache.stamp' now = time.time() try: # Prune at most once every hour if now - os.path.getmtime(stamp) < 3600: exit(0) except OSError: # It's OK if the file did not exist. # We will create it. pass # Erase all directories that are older than 2 hours, and all # files that have only one hardlinks. Files that have more than # one hardlinks are referenced to by directories; so the hardlink # count will decrease when the directory is purged. os.system('find ' + imgdir + ' -mindepth 1 -maxdepth 1 -mmin +120 ' + '\( -type d -o -links 1 \) -exec rm -rf {} +') # Create or update the stamp so we know when to run the next prune. open(stamp, "wb", 0) exit(0) # Assume Spot is installed sys.path.insert(0, '@pythondir@') if ('SERVER_SOFTWARE' in os.environ and os.environ['SERVER_SOFTWARE'].startswith('SimpleHTTP')): # We might be running from the build tree (but it's not sure). # Add the build and source directories first in the search path. # If we are not in the right place, python will find the installed # libraries later. sys.path.insert(0, '@srcdir@/../.libs') sys.path.insert(0, '@srcdir@/..') sys.path.insert(0, '../.libs') sys.path.insert(0, '..') # Darwin needs some help in figuring out where non-installed libtool # libraries are (on this platform libtool encodes the expected final # path of dependent libraries in each library). m = '../.libs:@top_builddir@/src/.libs:@top_builddir@/buddy/src/.libs' os.environ['DYLD_LIBRARY_PATH'] = m try: # execfile('ltl2tgba.opt') no longuer work with Python 3. exec(compile(open("ltl2tgba.opt").read(), "ltl2tgba.opt", 'exec'), global_vars, local_vars) except IOError: pass import spot import buddy def alarm_handler(signum, frame): unbufprint("""
The script was aborted because it has been running for too long. Please try a shorter formula, or different options (not drawing automata usually helps). If you want to benchmark big formulae it is better to install Spot on your own computer.
\n""") finish(kill = True) def reset_alarm(): signal.alarm(30) def render_dot(basename): unbufprint('' + dont_run_dot + ''' to be rendered on-line. However you may download the source in dot format and render it yourself.
\n') else: render_dot(autprefix) def render_automaton(automaton, dont_run_dot, issba, deco = False): dotsrc = spot.ostringstream() if not deco: spot.dotty_reachable(dotsrc, automaton, issba) else: spot.dotty_reachable(dotsrc, automaton, issba, deco) render_dot_maybe(dotsrc.str().encode('utf-8'), dont_run_dot) def render_formula(f): dotsrc = spot.ostringstream() spot.dotty(dotsrc, f) render_dot_maybe(dotsrc.str(), False) def print_stats(automaton): stats = spot.stats_reachable(automaton) unbufprint("%d state" % stats.states) if stats.states > 1: unbufprint("s") unbufprint(", %d transition" % stats.transitions) if stats.transitions > 1: unbufprint("s") count = automaton.number_of_acceptance_conditions() if count > 0: unbufprint(", %d acceptance condition" % count) if count > 1: unbufprint("s") acc = automaton.all_acceptance_conditions() unbufprint(": " + spot.bdd_format_accset(automaton.get_dict(), acc)) else: unbufprint(", no acceptance condition (all cycles are accepting)") unbufprint("
\n") # Decide whether we will render the automaton or not. # (A webserver is not a computation center...) if stats.states > 64: return "Automaton has too much states" if float(stats.transitions)/stats.states > 10: return "Automaton has too much transitions per state" return False form = cgi.FieldStorage() output_type = form.getfirst('o', 'v'); # Version requested. if output_type == 'v': unbufprint('Spot version %s\n' % spot.version()) finish() spot.unblock_signal(signal.SIGALRM) spot.unblock_signal(signal.SIGTERM) os.setpgrp() signal.signal(signal.SIGALRM, alarm_handler) reset_alarm() formula = form.getfirst('f', '') env = spot.default_environment.instance() pel = spot.empty_parse_error_list() f = spot.parse(formula, pel, env) if pel: unbufprint('