#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>

#if defined __GNUC__ && defined __sgi
# define _POSIX_C_SOURCE 199309L
#endif // __GNUC__ && __sgi

#ifndef unix
# if defined __unix||defined __unix__
#  define unix
# elif defined _AIX||defined __NetBSD__||defined __APPLE__
#  define unix
# endif
#endif

#ifdef __WIN32
# undef __STRICT_ANSI__
# include <sys/types.h>
#else // __WIN32
# include <sys/types.h>
# include <sys/signal.h>
# include <sys/errno.h>
#endif // __WIN32
#include <signal.h>
#include <errno.h>
#include <sys/stat.h>
#include <time.h>
#ifdef unix
# include <sys/times.h>
# include <sys/wait.h>
#endif // unix
#ifdef __WIN32
# include <windows.h>
# include <io.h>
# undef mkdir
# define mkdir(name,perm) _mkdir (name)
# include <libiberty/getopt.h>
# ifndef HAS_GETOPT_LONG
#  define HAS_GETOPT_LONG
# endif // HAS_GETOPT_LONG
#elif defined HAS_GETOPT_LONG
# include <getopt.h>
#endif // __WIN32

#ifdef HAS_REGEX
# include <regex.h>
#endif // HAS_REGEX

#ifdef EXPR_COMPILE
# include "Compilation.h"
#endif // EXPR_COMPILE

#include <unistd.h>

#include "version.h"
#include "Dotty.h"

#include "Expression.h"
#include "Valuation.h"
#include "GlobalMarking.h"
#include "Transition.h"
#include "Search.h"

#include "server.h"
/** number of analyzer jobs to fork at once (0=use single process) */
static unsigned jobs = 0;
/** Connection for distributed safety property analysis (0=none) */
static struct sockaddr_in* connect_addr;

/** @mainpage Maria: Modular Reachability Analyser for Algebraic System Nets
 * @htmlonly
 * Please visit also the
 * <a href="http://www.tcs.hut.fi/maria/">Maria home page</a>.
 * @endhtmlonly
 */

/**
 * @file maria.C
 * The main program of Maria
 */

/* Copyright © 1999-2003 Marko Mäkelä (msmakela@tcs.hut.fi).

   This file is part of MARIA, a reachability analyzer and model checker
   for high-level Petri nets.

   MARIA 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, or (at your option)
   any later version.

   MARIA 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.

   The GNU General Public License is often shipped with GNU software, and
   is generally kept in a file called COPYING or LICENSE.  If you do not
   have a copy of the license, write to the Free Software Foundation,
   59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#ifdef HAS_GETOPT_LONG
/** A unified interface to getopt(3) and the GNU variant getopt_long(3) */
# define getopt(argc,argv,optstring) getopt_long(argc,argv,optstring,opts,0)
static const struct option opts[] = {
  { "include", 1, 0, 'I' },
  { "define", 1, 0, 'D' },
  { "unfold", 1, 0, 'u' },
  { "undefine", 1, 0, 'U' },
  { "help", 0, 0, '?' },
  { "verbose", 0, 0, 'v' },
  { "version", 0, 0, 'V' },
  { "warnings", 0, 0, 'W' },
  { "no-warnings", 0, 0, 'w' },
#ifndef BUILTIN_LTL
  { "property-translator", 1, 0, 'p' },
#endif // BUILTIN_LTL
  { "array-limit", 1, 0, 'a' },
  { "quantification-limit", 1, 0, 'q' },
  { "breadth-first-search", 1, 0, 'b' },
  { "depth-first-search", 1, 0, 'd' },
#if defined __WIN32 || defined WIN32
#else /* __WIN32 || WIN32 */
  { "jobs", 1, 0, 'j' },
#endif /* __WIN32 || WIN32 */
  { "connect", 1, 0, 'k' },
  { "model", 1, 0, 'm' },
  { "md5-compacted", 1, 0, 'M' },
  { "probabilistic", 1, 0, 'P' },
  { "lossless", 1, 0, 'L' },
  { "hashes", 1, 0, 'H' },
  { "modular", 0, 0, 'R' },
  { "no-modular", 0, 0, 'r' },
  { "compress-hidden", 0, 0, 'Y' },
  { "no-compress-hidden", 0, 0, 'y' },
  { "compress-paths", 0, 0, 'Z' },
  { "no-compress-paths", 0, 0, 'z' },
  { "graph", 1, 0, 'g' },
  { "execute", 1, 0, 'e' },
  { "edges", 1, 0, 'E' },
  { "tolerance", 1, 0, 't' },
  { "width", 1, 0, 'i' },
  { "radix", 1, 0, 'x' },
# ifdef HAS_REGEX
  { "name", 1, 0, 'N' },
  { "no-name", 1, 0, 'n' },
# endif // HAS_REGEX
# ifdef EXPR_COMPILE
  { "compile", 1, 0, 'C' },
  { "no-compile", 0, 0, 'c' },
# endif // EXPR_COMPILE
  { 0, 0, 0, 0 }
};
#endif // HAS_GETOPT_LONG

/** Flag: be verbose */
static bool verbose = false;
#ifndef BUILTIN_LTL
/** Translator for property automata */
char* translator = 0;
#endif // !BUILTIN_LTL
#ifdef EXPR_COMPILE
/** name of the compilation directory (0=don't compile) */
char* compiledir = 0;
/** The compiled net */
static class Compilation* compilation = 0;
/** Command-line flags for compiled expressions */
# define EXPRFLAGS "cC:"
#else
/** Command-line flags for compiled expressions */
# define EXPRFLAGS ""
#endif // EXPR_COMPILE

#ifdef HAS_REGEX
/** regular expression identifiers */
static const char regexps[] = {
  'P', // places
  'T', // transitions
  't', // types
  'e', // enumeration names
  'c', // struct or union components
  'f', // functions
  'p', // parameters
  'v', // variables
  'i', // iterator variables
};
/** regular expressions for allowed names */
static regex_t* regexpAllow[sizeof regexps] = { 0, };
/** regular expressions for disallowed names */
static regex_t* regexpDisallow[sizeof regexps] = { 0, };
/** extract regular expressions from a string
 * @param s	a regular expression prefixed with a character
 * @param regex	compiled regular expressions
 */
static void regexpExtract (const char* s, regex_t** regex);
/** deallocate compiled regular expressions
 * @param regex		the compiled regular expressions
 */
static void regexpFree (regex_t** regex);
/** Command-line flags for specifying regular expressions */
# define REGEXFLAGS "n:N:"
#else // HAS_REGEX
/** Command-line flags for specifying regular expressions */
# define REGEXFLAGS ""
#endif // HAS_REGEX

/** Option string for getopt(3) */
static const
char optstring[] =
"I:D:u:U:?hvVwW"
#ifndef BUILTIN_LTL
"p:"
#endif // BUILTIN_LTL
"a:q:b:d:m:M:P:L:H:rRyYzZg:e:E:t:i:x:"
#if defined __WIN32 || defined WIN32
#else /* __WIN32 || WIN32 */
"j:"
#endif /* __WIN32 || WIN32 */
"k:" EXPRFLAGS REGEXFLAGS;

#include "util.h"
#include "NameList.h"
static class NameList includePath;

#include "Printer.h"
#include "Net.h"
#include "Graph.h"
#include "ComponentGraph.h"
#include "LSTS.h"
#include "Property.h"
#include "Product.h"
#include "LNet.h"
#include "HashGraph.h"
#include "CompactSet.h"
#include "ParSet.h"
#include "FullSet.h"
#include "GraphReporter.h"
#include "StateSetReporter.h"

/** The Bison-generated Petri Net parser
 * @return	zero if exiting due to end-of-file; 1 if parse error occurred
 */
extern int pnparse ();
/** message prefix function for the Petri Net parser
 * @return	number of characters written
 */
extern unsigned pnmsg (void);

/** The Bison-generated scripting language parser
 * @return	zero if exiting due to end-of-file; 1 if parse error occurred
 */
extern int deparse ();
/** Read a command line */
extern const char* deline ();
/** Reset the scripting language parser */
extern void dereset ();
/** message prefix function for the script parser
 * @return	number of characters written
 */
extern unsigned demsg (void);

/** Define a preprocessor symbol
 * @param sym	name of the symbol
 */
extern void define_sym (const char* sym);
/** Undefine a preprocessor symbol
 * @param sym	name of the symbol
 */
extern void undef_sym (const char* sym);

/** warning flag (true=print warnings) */
extern bool pnwarnings;
/** Maximum index type size of arrays */
extern card_t maxIndex;
/** Maximum range size of quantifications */
extern card_t maxRange;

/** The printer object */
class Printer thePrinter;
/** The Petri Net being parsed */
class Net* net = 0;
/** Flag: has a complaint been made about a net not being loaded? */
bool notloaded = false;
/** Flag: apply modular analysis */
bool modular = false;
/** The reachability graph interface */
static class GraphReporter* graphreporter = 0;
/** Selected Petri Net */
class Net* subnet = 0;
/** Selected Petri Net as a path from the root net */
unsigned* modulepath = 0;
/** Name of the Petri Net being parsed */
const char* netname;
/** Number of elements in the hash tables in probabilistic analysis */
static unsigned hashSize = 0x7ffff;
/** Number of entries in the hash functions in probabilistic analysis */
static unsigned funcSize = 16;
/** Number of hash tables in probabilistic analysis */
static unsigned numTables = 2;
/** Flag: use probabilistic search */
static bool probabilistic = false;
/** Flag: use MD5-compacted probabilistic search */
static bool md5compact = false;
/** Flag: eliminate local behaviour */
static bool local = false;
/** Flag: apply path compression */
static bool compress = false;
/** Report state space sizes after generating this many new edges */
static unsigned thresold;
/** Allow this many errors */
static unsigned maxerrors;

/** Name of the script being parsed */
const char* scriptname;
/** Name of scripts parsed from the command line */
const char* scriptcommand = "(command line)";
/** The script to be parsed from the command line */
const char* scriptstring;

/** Name of the program */
static const char* progname;

/** Check the validity of an identifier
 * @param name		name of the identifier
 * @param type		type of the identifier
 * @param msg		error message
 */
void checkName (const char* name, unsigned type, const char* msg);

/** Open a model
 * @param filename	name of the net to be analyzed
 * @param graphfile	base name for the reachability graph files
 * @param regen		flag: regenerate the graph files
 *			(if !graphfile && !regen, generate no graph files)
 * @return		false on error, true otherwise
 */
bool model (const char* filename, const char* graphfile, bool regen);

/** Analyze the safety properties of the model
 * @param breadth	flag: search breadth first instead of depth
 * @param visual	visualization level for counterexample
 * @param prop		the safety property to analyze (optional)
 */
static void safetyP (bool breadth, card_t visual, const class Property* prop);

/** Analyze the safety properties of the model
 * @param breadth	flag: search breadth first instead of depth
 * @param expr		the safety formula to analyze (optional)
 * @param visual	visualization level for counterexample
 */
void safety (bool breadth, class Expression* expr, card_t visual);

/** Perform on-the-fly model checking
 * @param state		state to start the analysis with
 * @param expr		formula to be verified
 * @param visual	visualization level
 */
static void onthefly (card_t state, class Expression* expr, card_t visual);

/** Generate an LSTS of the model when generating the state space
 * @param filebase	base file name (0=disable LSTS generation)
 */
void lsts (const char* filebase);

/** Unfold the model
 * @param ffilename	output format and file name (0=default,stdout)
 */
void unfold (const char* ffilename);

/** Dump the syntax tree of the model
 * @param visual	visualization level
 */
void dump (card_t visual);

/** Dump the reachability graph of the model
 * @param visual	visualization level
 */
void dumpgraph (card_t visual);

/** Perform exhaustive reachability analysis
 * @param state		state to start the analysis with
 * @param breadth	flag: go breadth-first instead of depth-first
 */
void analyze (card_t state, bool breadth);

/** Open an input file
 * @param name		name of the input file
 * @return		a descriptor to the file, or 0 on error
 */
FILE* openfile (const char* name);
/** Open a script
 * @param name		name of the script
 * @return		a descriptor to the file, or 0 on error
 */
FILE* openscript (const char* name);

/** Execute a script
 * @param filename	name of the script to be executed
 * @return		false on a fatal error, true otherwise
 */
static bool execute (const char* filename);
/** Display a help message of the command line options */
static void cmdhelp (void);
/** Check if graphless model checking is selected
 * @return	true if purely state-based approach should be applied
 */
bool isGraphless ();
/** Check if a state number exists
 * @param state	the state
 * @return	true if the state exists
 */
bool isState (card_t state);
/** Show a state
 * @param state		the state
 * @param visual	visualization level
 */
void show (card_t state, card_t visual);
/** Show a path specified as a state list
 * @param states	the state list
 * @param visual	visualization level
 */
void show (const card_t* states, card_t visual);
/** Show the successors of a state
 * @param state		the state
 * @param seq		flag: fully expand deterministic sequences
 * @param visual	visualization level
 */
void succ (card_t state, bool seq, card_t visual);
/** Show the predecessors of a state
 * @param state		the state
 * @param seq		flag: fully expand deterministic sequences
 * @param visual	visualization level
 */
void pred (card_t state, bool seq, card_t visual);
/** Display statistics */
void stats (void);
/** Display disabled enabledness set members
 * @param log	the enabledness sets
 */
static void showEnabled (const char* log);

/** Time used by the program since the last measurement */
static clock_t lastclock;

/** Display execution times */
void showtimes (void);
/** Compute strongly connected components
 * @param state	state in which to start the search
 * @param cond	condition for the states to be included in the search
 */
void strong (card_t state, class Expression* cond);
/** Display non-trivial terminal strongly connected components */
void terminal (void);
/** Display all strongly connected components
 * @param visual	visualization level
 */
void allcomps (card_t visual);
/** Show the nodes of a strongly connected component fulfilling a condition
 * @param comp		the component
 * @param cond		the condition (0=true)
 * @param visual	visualization level
 */
void comp_show (card_t comp,
		class Expression* cond,
		card_t visual);

/** Show the successors of a strongly connected component
 * @param comp		the component
 * @param visual	visualization level
 */
void comp_succ (card_t comp, card_t visual);
/** Show the predecessors of a strongly connected component
 * @param comp		the component
 * @param visual	visualization level
 */
void comp_pred (card_t comp, card_t visual);
/** Display a labelled state transition system of a path
 * @param graph		the graph
 * @param path		the path
 * @param reverse	flag: display the path in reverse order?
 */
static void
printLSTS (const class Graph& graph,
	   const card_t* path,
	   bool reverse);
/** Display a path
 * @param path	the path
 */
static void printPath (const card_t* path);
/** Display a path in reverse order
 * @param path	the path
 */
static void printReversePath (const card_t* path);
/** Show the shortest path from a state to a strongly connected component
 * @param state		the state
 * @param comp		the component
 * @param pathc		path condition (must hold in every state on the path)
 * @param visual	visualization level
 */
void path_sc (card_t state, card_t comp,
	      class Expression* pathc,
	      card_t visual);
/** Show the shortest path from a strongly connected component to a state
 * @param comp		the component
 * @param state		the state
 * @param pathc		path condition (must hold in every state on the path)
 * @param visual	visualization level
 */
void path_cs (card_t comp, card_t state,
	      class Expression* pathc,
	      card_t visual);
/** Show the shortest path from a state to a state
 * @param state		the source state
 * @param dest		the target state
 * @param pathc		path condition (must hold in every state on the path)
 * @param visual	visualization level
 */
void path_ss (card_t state, card_t dest,
	      class Expression* pathc,
	      card_t visual);
/** Show the shortest path from a state to a state where a condition holds
 * @param state		the source state
 * @param cond		the condition
 * @param pathc		path condition (must hold in every state on the path)
 * @param visual	visualization level
 */
void path_se (card_t state, class Expression& cond,
	      class Expression* pathc,
	      card_t visual);
/** Show the shortest path from a state where a condition holds to a state
 * @param state		the target state
 * @param cond		the condition
 * @param pathc		path condition (must hold in every state on the path)
 * @param visual	visualization level
 */
void rpath_se (card_t state, class Expression& cond,
	       class Expression* pathc,
	       card_t visual);
/** Show the shortest path from a state to a loop state, plus the loop
 * @param state		the source state
 * @param loop		amount and number of states in the loop
 * @param pathc		path condition (must hold in every state on the path)
 * @param visual	visualization level
 */
void path_sl (card_t state,
	      const card_t* loop,
	      class Expression* pathc,
	      card_t visual);

/** Fire an anonymous transition
 * @param trans	the anonymous transition
 * @param state	state in which to fire the transition
 * @param visual	visualization level for counterexample
 */
void fireTransition (class Transition& trans, card_t state, card_t visual);

/** Evaluate a formula in the current state
 * @param state		state in which to evaluate the formula
 * @param expr		formula to be evaluated
 * @param visual	visualization level for counterexample
 */
void eval (card_t state, class Expression* expr, card_t visual);

/** Flag: has the analysis been interrupted? */
extern volatile bool interrupted;
#if defined __WIN32
#elif defined unix
/** Visualization process identifier */
extern volatile pid_t vispid;
/** Visualizer for counterexamples */
static char* visualizer = 0;
#endif // unix

#ifdef unix
/** Handle a child termination
 * @param pid		process number of the terminated child
 */
extern "C"
void
childterm (pid_t pid)
{
  if (pid == vispid)
    Dotty::stopVisual ();
}
#endif // unix

/** Handle a signal
 * @param num		number of the signal
 */
extern "C"
void
sig (int num) {
  if (num == SIGINT)
    interrupted = true;
#ifdef unix
  else if (num == SIGCHLD)
    childterm (wait (0));
#endif // unix
  signal (num, sig);
}

/** Clean everything up at program exit */
extern "C"
void
cleanup (void) {
  dereset ();
#ifdef HAS_REGEX
  regexpFree (regexpAllow);
  regexpFree (regexpDisallow);
#endif // HAS_REGEX
#ifdef EXPR_COMPILE
  delete compilation;
  delete[] compiledir;
#endif // EXPR_COMPILE
#ifdef unix
  if (vispid) {
    kill (vispid, SIGKILL);
    Dotty::stopVisual ();
  }
  delete[] visualizer;
#endif // unix
  if (graphreporter) {
    delete &graphreporter->getGraph ();
    delete graphreporter;
  }
  delete net;
  delete[] modulepath;
#ifndef BUILTIN_LTL
  delete[] translator;
#endif // !BUILTIN_LTL
  FILE* f = thePrinter.getOutput ();
  if (f != stderr)
    fclose (f);
  comm_cleanup ();
}

/** The main function
 * @param argc	argument count
 * @param argv	argument vector
 * @return	0 if successful;
 *		1 on parse error
 */
int
main (int argc, char** argv)
{
#ifndef unix
  lastclock = clock ();
#else // !unix
  struct tms current;
  lastclock = times (&current);
#endif // !unix
  setlocale (LC_ALL, "");
  signal (SIGINT, sig);
#ifdef unix
  signal (SIGCHLD, sig);
#endif // unix
  atexit (cleanup);
  progname = *argv;

  /** flag: has the computation been aborted? */
  extern bool exiting;

#if defined YYDEBUG && YYDEBUG
  {
    /** Debugging flag for the Bison-generated parser */
    extern int pndebug;
    char *debug = getenv ("DEBUG");
    if (debug) pndebug = atoi (debug);
  }
#endif // YYDEBUG

#ifndef __WIN32
  const size_t prognamelen = strlen (progname);
  visualizer = new char[prognamelen + 5];
  memcpy (visualizer, progname, prognamelen);
  memcpy (visualizer + prognamelen, "-vis", 5);
  Dotty::useVisual (visualizer);
#endif // __WIN32

  for (;;) {
    int c = getopt (argc, argv, optstring);
    if (c == EOF)
      break;
    switch (c) {
    case ':':
      // fall through
    default:
    case '?':
    case 'h':
      cmdhelp ();
      return 0;
    case 'v':
      verbose = true;
      break;
    case 'V':
      fprintf (stderr, "%s version " VERSION "\n", progname);
      return 0;
    case 'w':
      pnwarnings = false;
      break;
    case 'W':
      pnwarnings = true;
      break;
    case 'd':
      if (model (optarg, 0, true))
	analyze (0, false);
      break;
    case 'b':
      if (model (optarg, 0, true))
	analyze (0, true);
      break;
    case 'g':
      model (0, optarg, false);
      break;
    case 'm':
      model (optarg, 0, true);
      break;
    case 'M':
    case 'P':
    case 'L':
      model (optarg, 0, false);
      md5compact = c == 'M';
      probabilistic = c != 'L';
      break;
    case 'H':
      if (sscanf (optarg, "%u%n,%u%n,%u %n",
		  &hashSize, &c, &funcSize, &c, &numTables, &c) < 1 ||
	  optarg[c] ||
	  !hashSize || !funcSize || !numTables) {
	fprintf (stderr, "error in hash parameters: `%s'\n", optarg);
	return 1;
      }
    case 'r':
      modular = false;
      subnet = net; delete[] modulepath; modulepath = 0;
      break;
    case 'R':
      modular = true;
      break;
    case 'y':
      local = false;
      break;
    case 'Y':
      local = true;
      break;
    case 'z':
      compress = false;
      break;
    case 'Z':
      compress = true;
      break;
#ifndef BUILTIN_LTL
    case 'p':
      delete[] translator;
      translator = newString (optarg);
      break;
#endif // !BUILTIN_LTL
    case 'a':
      {
	char* end = 0;
	maxIndex = strtoul (optarg, &end, 0);
	if (!*optarg || *end) {
	  fprintf (stderr, "error in array size limit: `%s'\n", optarg);
	  return 1;
	}
      }
      break;
    case 'q':
      {
	char* end = 0;
	maxRange = strtoul (optarg, &end, 0);
	if (!*optarg || *end) {
	  fprintf (stderr, "error in quantification limit: `%s'\n", optarg);
	  return 1;
	}
      }
      break;
    case 'i':
      {
	char* end = 0;
	unsigned width = strtoul (optarg, &end, 0);
	if (!*optarg || *end) {
	  fprintf (stderr, "error in output width limit: `%s'\n", optarg);
	  return 1;
	}
	else
	  thePrinter.setWidth (width);
      }
      break;
    case 'x':
      {
	if (!strcmp (optarg, "oct") || !strcmp (optarg, "octal") ||
	    !strcmp (optarg, "8"))
	  thePrinter.setRadix (Printer::Octal);
	else if (!strcmp (optarg, "hex") || !strcmp (optarg, "hexadecimal") ||
		 !strcmp (optarg, "16"))
	  thePrinter.setRadix (Printer::Hexadecimal);
	else if (!strcmp (optarg, "dec") || !strcmp (optarg, "decimal") ||
		 !strcmp (optarg, "10"))
	  thePrinter.setRadix (Printer::Decimal);
	else
	  fprintf (stderr, "ignoring unknown radix setting: `%s'\n", optarg);
      }
      break;
#if defined __WIN32 || defined WIN32
#else /* __WIN32 || WIN32 */
    case 'j':
      {
	char* end = 0;
	unsigned num = strtoul (optarg, &end, 0);
	if (!*optarg || *end) {
	  fprintf (stderr, "error in number of jobs: `%s'\n", optarg);
	  return 1;
	}
	else
	  jobs = num;
      }
      break;
#endif /* __WIN32 || WIN32 */
    case 'k':
      if (!(connect_addr = resolve (optarg)))
	return 1;
      break;
    case 'I':
      includePath.append (newString (optarg));
      break;
    case 'u':
      unfold (optarg);
      break;
    case 'D':
      define_sym (optarg);
      break;
    case 'U':
      undef_sym (optarg);
      break;
    case 'e':
      if ((scriptstring = optarg) &&
	  (interrupted || !execute (scriptcommand)))
	return 4;
      if (exiting)
	return 0;
      break;
    case 'E':
      {
	char* end = 0;
	thresold = strtoul (optarg, &end, 0);
	if (!*optarg || *end) {
	  fprintf (stderr, "error in edge thresold: `%s'\n", optarg);
	  return 1;
	}
      }
      break;
    case 't':
      {
	char* end = 0;
	maxerrors = strtoul (optarg, &end, 0);
	if (!*optarg || *end) {
	  fprintf (stderr, "error in error tolerance: `%s'\n", optarg);
	  return 1;
	}
      }
      break;
#ifdef HAS_REGEX
    case 'n':
      regexpExtract (optarg, regexpDisallow);
      break;
    case 'N':
      regexpExtract (optarg, regexpAllow);
      break;
#endif // HAS_REGEX
#ifdef EXPR_COMPILE
    case 'c':
      delete[] compiledir;
      compiledir = 0;
      break;
    case 'C':
      delete[] compiledir;
      compiledir = newString (optarg);
      break;
#endif // EXPR_COMPILE
    }
  }

  for (int i = optind; i < argc; i++)
    if (exiting)
      return 0;
    else if (interrupted || !execute (argv[i]))
      return 4;

  dereset ();
  while (!exiting && (scriptstring = deline ()))
    execute (scriptcommand);

  return 0;
}

static bool
execute (const char* filename)
{
  /** flag: has the computation been aborted? */
  extern bool exiting;
  scriptname = filename;
  thePrinter.setBOL (scriptname == scriptcommand ? 0 : &demsg);
  dereset ();
  bool status = !deparse () || exiting;
  thePrinter.setBOL (0);
  return status;
}

#ifdef HAS_REGEX
static void
regexpExtract (const char* s, regex_t** regex)
{
  unsigned i;
  for (i = 0; i < sizeof regexps; i++)
    if (*s == regexps[i])
      break;
  if (i < sizeof regexps) {
    if (regex[i]) {
      regfree (regex[i]);
      regex[i] = 0;
    }
    if (int errcode = regcomp (regex[i] = new regex_t,
			       &s[1], REG_EXTENDED | REG_NOSUB)) {
      size_t size = regerror (errcode, regex[i], 0, 0);
      char* msg = new char[size];
      regerror (errcode, regex[i], msg, size);
      fprintf (stderr, "%s:invalid regular expression '%c' \"%s\": %s\n",
	       progname, *s, &s[1], msg);
      delete[] msg;
      delete regex[i];
      regex[i] = 0;
    }
  }
  else
    fprintf (stderr, "%s:unknown regular expression '%c'\n", progname, *s);
}

static void
regexpFree (regex_t** regex)
{
  for (unsigned i = sizeof regexps; i--; ) {
    if (regex[i]) {
      regfree (regex[i]);
      delete regex[i];
    }
  }
}

void
checkName (const char* name, unsigned type, const char* msg)
{
  assert (name && type < sizeof regexps && msg);
  if (regexpAllow[type] &&
      !regexec (regexpAllow[type], name, 0, 0, 0))
    return;
  if (regexpDisallow[type] &&
      !regexec (regexpDisallow[type], name, 0, 0, 0)) {
    thePrinter.printRaw (msg);
    thePrinter.print (name);
    thePrinter.finish ();
    /** number of errors encountered */
    extern unsigned pnerrors;
    pnerrors++;
  }
}
#else // HAS_REGEX
void
checkName (const char*, unsigned, const char*)
{
}
#endif // HAS_REGEX

static void
cmdhelp (void)
{
  fprintf (stderr, "Usage: %s [options] scriptfile...\n", progname);
  fputs ("-a\t--array-limit LIMIT\n"
	 "-b\t--breadth-first-search MODELNAME\n"
#ifdef EXPR_COMPILE
	 "-C\t--compile DIRECTORY\n"
	 "-c\t--no-compile\n"
#endif // EXPR_COMPILE
	 "-D\t--define SYMBOL\n"
	 "-d\t--depth-first-search MODELNAME\n"
	 "-E\t--edges number\n"
	 "-e\t--execute SCRIPT\n"
	 "-g\t--graph GRAPHFILE\n"
	 "-H\t--hashes h[,f[,t]]\n"
	 "-h\t--help\n"
	 "-I\t--include DIRECTORY\n"
	 "-i\t--width columns\n"
#if defined __WIN32 || defined WIN32
#else /* __WIN32 || WIN32 */
	 "-j\t--jobs NUMBER\n"
#endif /* __WIN32 || WIN32 */
	 "-k\t--connect PORT[/HOST]\n"
	 "-L\t--lossless MODELNAME\n"
	 "-m\t--model MODELNAME\n"
	 "-M\t--md5-compacted MODELNAME\n"
#ifdef HAS_REGEX
	 "-N\t--name [PTtecfpvi]RE\n"
	 "-n\t--no-name [PTtecfpvi]RE\n"
#endif // HAS_REGEX
	 "-P\t--probabilistic MODELNAME\n"
#ifndef BUILTIN_LTL
	 "-p\t--property-translator COMMAND\n"
#endif // BUILTIN_LTL
	 "-q\t--quantification-limit LIMIT\n"
	 "-R\t--modular\n"
	 "-r\t--no-modular\n"
	 "-t\t--tolerance NUMBER\n"
	 "-u\t--unfold M?([lmpr](OUTFILE)?)?\n"
	 "-U\t--undefine SYMBOL\n"
	 "-v\t--verbose\n"
	 "-V\t--version\n"
	 "-W\t--warnings\n"
	 "-w\t--no-warnings\n"
	 "-x\t--radix BASE\n"
	 "-y\t--no-compress-hidden\n"
	 "-Y\t--compress-hidden\n"
	 "-z\t--no-compress-paths\n"
	 "-Z\t--compress-paths\n",
	 stderr);
}

bool
model (const char* filename, const char* graphfile, bool regen)
{
  assert (filename || graphfile);
  thePrinter.setBOL (&pnmsg);
#ifdef EXPR_COMPILE
  delete compilation;
  compilation = 0;
#endif // EXPR_COMPILE
  delete net;
  delete[] modulepath;
  modulepath = 0; subnet = 0;
  notloaded = false;
  net = new class Net;
  if (graphreporter) {
    delete &graphreporter->getGraph ();
    delete graphreporter;
  }
  graphreporter = 0;
  class Graph* graph = 0;

  if (filename) {
    if (FILE* f = fopen (filename, "rb"))
      fclose (f);
    else {
      perror (filename);
      goto notOpen;
    }
  }

  if (graphfile || regen) {
    graph = new class Graph (*net, filename, graphfile);
    if (!graph->openFiles (regen)) {
      fprintf (stderr, "%s:could not open the graph files\n",
	       graphfile ? graphfile : filename);
    notOpen:
      delete net;
      net = 0;
      delete graph;
      graph = 0;
      return false;
    }
    if (!filename)
      filename = graph->getFilename ();
  }

  netname = filename;
  if (FILE* f = fopen (netname, "rb"))
    fclose (f);
  else {
    perror (netname);
    goto notOpen;
  }

  /** number of errors encountered */
  extern unsigned pnerrors;
  pnerrors = 0;
  if (pnparse () || pnerrors) {
  errors:
    fprintf (stderr, "%s:%u %s\n", netname, pnerrors,
	     pnerrors == 1 ? "error" : "errors");
    delete net;
    net = 0;
    delete graph;
    graph = 0;
    thePrinter.setBOL (0);
    return false;
  }

  thePrinter.setBOL (0);
  if (!net->prepareModular (thePrinter))
    goto errors;

  if (verbose) {
    fprintf (stderr, "%s:%u places and %u transitions\n",
	     netname, net->getNumPlaces (), net->getNumTransitions ());
    fprintf (stderr, "%s:computing the initial marking\n", netname);
  }
  if (!net->computeInitMarking (thePrinter))
    fprintf (stderr, "%s:error in the initial marking\n", netname);
  else {
    class BitPacker buf;
    class Graph::AddStatus status (false, 0);
    if (!net->getInitMarking ()->encode (buf, *net->getInitMarking (), 0))
      fprintf (stderr, "%s:invalid initial marking\n", netname);
    else if (buf.deflate (),
	     graph && (status = graph->add (buf.getBuf (), buf.getNumBytes ()),
		       status.number))
      fprintf (stderr, "%s:inconsistent reachability graph file\n", netname);
#ifdef EXPR_COMPILE
    else if (compiledir) {
      if (verbose)
	fprintf (stderr, "%s:compiling\n", netname);
      compilation = new class Compilation (*net, compiledir, progname);
      if (!compilation->compile ())
	fprintf (stderr, "%s:compilation failed\n", netname);
      else if (char* errmsg = compilation->link ()) {
	fprintf (stderr, "%s:%s\n", netname, errmsg);
	delete[] errmsg;
      }
      else
	goto success;
    }
#endif // EXPR_COMPILE
    else {
#ifdef EXPR_COMPILE
    success:
#endif // EXPR_COMPILE
      if (graph)
	graphreporter =
	  new class GraphReporter (
#ifdef EXPR_COMPILE
				   compilation,
#endif // EXPR_COMPILE
				   *net, thePrinter, maxerrors,
				   compress, local, !modular,
				   thresold, *graph);
      subnet = net;
      return true;
    }
  }

#ifdef EXPR_COMPILE
  delete compilation;
  compilation = 0;
#endif // EXPR_COMPILE
  delete graph;
  graph = 0;
  delete net;
  net = 0;
  return false;
}

static void
safetyP (bool breadth,
	 card_t visual,
	 const class Property* prop)
{
  if (!net) {
    if (!notloaded) {
      notloaded = true;
      thePrinter.printRaw ("no model has been loaded");
      thePrinter.finish ();
    }
    return;
  }
  else if (subnet != net) {
    thePrinter.printRaw ("need to be in root net");
    thePrinter.finish ();
    return;
  }

  class StateSet* states;
  /** apply path compression? */
  bool comp = compress;
  /** suppress local (transient) states? */
  bool loc = local;
  /** apply distributed search? */
  bool distributed = connect_addr || jobs;
  /** client socket number */
  int s = 0;

  if (connect_addr) {
    if (!(s = client_connect ()))
      comp = false; // this is the server
    else if (s > 0)
      goto client;
    else // failed to establish a connection
      return;
  }
  else if (jobs)
    comp = false;

  if (probabilistic && md5compact) {
    class CompactSet* cs = new class CompactSet (hashSize, funcSize);
    states = cs;
    if (!cs->init ()) {
      thePrinter.printRaw ("insufficient memory for ");
      thePrinter.print (card_t (cs->getHashSize ()));
      thePrinter.printRaw (" hash table entries of ");
      thePrinter.print (card_t (funcSize));
      thePrinter.printRaw (" bytes");
      thePrinter.finish ();
      goto done;
    }

    if (verbose)
      fprintf (stderr, "%s:using hash compaction -H%u,%u\n",
	       netname, cs->getHashSize (), funcSize);
  }
  else if (probabilistic) {
    if (!static_cast<class HashGraph*>
	(states = new class HashGraph)->init
	(numTables, funcSize, hashSize)) {
      thePrinter.printRaw ("insufficient memory for ");
      thePrinter.print (card_t (numTables));
      thePrinter.printRaw (" tables of ");
      thePrinter.print (card_t (funcSize));
      thePrinter.printRaw (" functions and ");
      thePrinter.print (card_t (hashSize));
      thePrinter.printRaw (" bits");
      thePrinter.finish ();
      goto done;
    }

    if (verbose)
      fprintf (stderr, "%s:using bitstate hashing -H%u,%u,%u\n",
	       netname, hashSize, funcSize, numTables);
  }
  else {
    if (!static_cast<class FullSet*>
	(states = new class FullSet)->init (true))
      goto done;
    if (verbose)
      fprintf (stderr, "%s:exploring the reachable states\n", netname);
  }

#ifdef EXPR_COMPILE
  if (compilation) {
    if (prop) {
      if (verbose)
	fprintf (stderr, "%s:compiling the property automaton\n", netname);
      if (!compilation->compile (*prop)) {
	thePrinter.printRaw ("could not compile the property automaton");
	thePrinter.finish ();
	return;
      }
    }
    else
      compilation->clearProp ();
  }
#endif // EXPR_COMPILE

  if (distributed) {
    bool server;
    if (0 > (s = createJobs (jobs, server))) {
      if (!server)
	exit (-1);
      else
	return;
    }
    if (server) {
      loc = false;
      goto server;
    }
    delete states;
  client:
    states = new class ParSet (s);
  }

  {
    class BitPacker buf;
    if (!net->getInitMarking ()->encode (buf, *net->getInitMarking (), 0)) {
      fprintf (stderr, "%s:invalid initial marking\n", netname);
      delete states;
      if (distributed)
	exit (0); // clients terminate here
      return;
    }
    else {
      buf.deflate ();
      if (!states->add (buf.getBuf (), buf.getNumBytes ()))
	assert (false);
    }
  }

  if (distributed)
    thresold = 0, visual = 0, distributed = false;
 server:
  {
    /** The state space interface */
    class StateSetReporter reporter (
#ifdef EXPR_COMPILE
				     compilation,
#endif // EXPR_COMPILE
				     *net, thePrinter, maxerrors,
				     comp, loc, !modular, thresold,
				     visual ? Dotty::startVisual () : 0,
				     visual > 1, *states, prop);
    thePrinter.setBOL (0);
    if (distributed)
      serve (s, reporter, breadth);
    else {
      reporter.analyze (breadth
			? StateReporter::Breadth
			: StateReporter::Depth);
      if (s) {
	delete states;
	exit (0); // clients exit here
      }
    }
    thePrinter.printQuoted (netname);
    if (unsigned num = states->getNumStates ()) {
      thePrinter.printRaw (probabilistic ? ": at least " : ": ");
      thePrinter.print (num);
      if (reporter.getMinSize () <= reporter.getMaxSize ()) {
	thePrinter.printRaw (" states (");
	thePrinter.print (card_t (reporter.getMinSize ()));
	if (reporter.getMaxSize () != reporter.getMinSize ()) {
	  thePrinter.printRaw ("..");
	  thePrinter.print (card_t (reporter.getMaxSize ()));
	}
	thePrinter.printRaw (" bytes), ");
      }
      else
	thePrinter.printRaw (" states, ");
    }
    else
      thePrinter.printRaw (": no states, ");
    if (reporter.getNumLocal ()) {
      thePrinter.printRaw ("at most ");
      thePrinter.print (reporter.getNumLocal ());
      thePrinter.printRaw (" module states, ");
    }
    if (reporter.getNumErrors ()) {
      thePrinter.print (card_t (reporter.getNumErrors ()));
      thePrinter.printRaw (reporter.getNumErrors () == 1
			   ? " error, "
			   : " errors, ");
    }
    thePrinter.print (states->getNumArcs ());
    if (probabilistic && md5compact) {
      thePrinter.printRaw (" arcs, ");
      thePrinter.print (card_t (static_cast<class CompactSet*>
				(states)->getNumCollisions ()));
      thePrinter.printRaw (" collisions");
    }
    else
      thePrinter.printRaw (" arcs");
    thePrinter.finish ();
    showEnabled (reporter.getEnabled ());
    thePrinter.setBOL (&demsg);
  }

 done:
  delete states;
}

/** Analyze the safety properties of the model
 * @param breadth	flag: search breadth first instead of depth
 * @param expr		the safety formula to analyze (optional)
 * @param visual	visualization level for counterexample
 */
void
safety (bool breadth, class Expression* expr, card_t visual)
{
  if (!net) {
    if (!notloaded) {
      notloaded = true;
      thePrinter.printRaw ("no model has been loaded");
      thePrinter.finish ();
    }
  }
  else if (subnet != net) {
    thePrinter.printRaw ("need to be in root net");
    thePrinter.finish ();
  }
  else if (!expr)
    safetyP (breadth, visual, 0);
#ifndef BUILTIN_LTL
  else if (!translator || !*translator) {
    thePrinter.printRaw ("no translator for property automata defined");
    thePrinter.finish ();
  }
#endif // !BUILTIN_LTL
  else {
#ifndef BUILTIN_LTL
    class Property property;
    if (!property.create (translator, *expr)) {
      thePrinter.printRaw ("could not translate the formula");
      thePrinter.finish ();
    }
    else if (!property.isFinite ()) {
#endif // !BUILTIN_LTL
      thePrinter.printRaw ("not a safety formula");
      thePrinter.finish ();
#ifndef BUILTIN_LTL
    }
    else
      safetyP (breadth, visual, &property);
#endif // !BUILTIN_LTL
  }
}

static void
onthefly (card_t state, class Expression* expr, card_t visual)
{
  if (net != subnet) {
    thePrinter.printRaw ("need to be in root net");
    thePrinter.finish ();
    return;
  }
#ifndef BUILTIN_LTL
  if (!translator || !*translator) {
    thePrinter.printRaw ("no translator for property automata defined");
    thePrinter.finish ();
  }
  else {
#endif // !BUILTIN_LTL
    if (verbose)
      fprintf (stderr, "%s:translating the formula\n", netname);
    class Property property;
    if (!property.create (
#ifndef BUILTIN_LTL
			  translator,
#endif // !BUILTIN_LTL
			  *expr)) {
      expr->destroy ();
      thePrinter.printRaw ("could not translate the formula");
      thePrinter.finish ();
    }
    else if (expr->destroy (),
	     !state && !graphreporter && property.isFinite ()) {
      if (verbose)
	fprintf (stderr, "%s:performing safety checking\n", netname);
      safetyP (true, visual, &property);
    }
    else if (isState (state)) {
      graphreporter->setAddOld (true);
      class Product product (*graphreporter, property);
      card_t* counterexample;
#ifdef EXPR_COMPILE
      if (compilation) {
	if (verbose)
	  fprintf (stderr, "%s:compiling the property automaton\n", netname);
	if (!compilation->compile (property)) {
	  thePrinter.printRaw ("could not compile the property automaton");
	  thePrinter.finish ();
	  return;
	}
      }
#endif /* EXPR_COMPILE */
      if (verbose)
	fprintf (stderr, "%s:performing model checking\n", netname);
      counterexample = product.analyze (state);
      graphreporter->setAddOld (false);
#ifdef EXPR_COMPILE
      if (compilation)
	compilation->clearProp ();
#endif /* EXPR_COMPILE */

      if (!interrupted) {
	if (!counterexample) {
	  thePrinter.printRaw ("property holds");
	  thePrinter.finish ();
	}
 	else if (class Dotty* d = visual ? Dotty::startVisual () : 0)
	  d->show (graphreporter->getChild (modulepath),
		   counterexample, visual > 1, true);
 	else {
 	  thePrinter.printRaw ("counterexample path:");
 	  thePrinter.linebreak ();
 	  printReversePath (counterexample);
	  thePrinter.finish ();
  	}
      }

      delete[] counterexample;
      stats ();
    }
#ifndef BUILTIN_LTL
  }
#endif // !BUILTIN_LTL
}

void
lsts (const char* filebase)
{
  if (!filebase) {
    if (graphreporter)
      const_cast<class Graph&>(graphreporter->getGraph ()).setLSTS (0);
    return;
  }
  if (net != subnet) {
    thePrinter.printRaw ("need to be in root net");
    thePrinter.finish ();
    return;
  }
  if (!isState (0))
    return;
  if (!graphreporter->isFlattened ()) {
    thePrinter.printRaw ("no LSTS output for modular state spaces");
    thePrinter.finish ();
    return;
  }
  class LSTS* l = new class LSTS (*net, filebase);
  if (l->openFiles ()) {
    const_cast<class Graph&>(graphreporter->getGraph ()).setLSTS (l);
    l->outputState (*net->getInitMarking (), 0);
  }
  else
    delete l;
}

void
unfold (const char* ffilename)
{
  /** output format */
  enum { fmtDefault, fmtLoLA, fmtPEP, fmtPROD } format = fmtDefault;
  /** flag: generate a minimal unfolding */
  bool minimal = false;
  /** primary and secondary output file */
  FILE* f = stdout, *f2 = stdout;

  if (!net) {
    if (!notloaded) {
      notloaded = true;
      thePrinter.printRaw ("no model has been loaded");
      thePrinter.finish ();
    }
    return;
  }
  else if (net != subnet) {
    thePrinter.printRaw ("need to be in root net");
    thePrinter.finish ();
    return;
  }

  if (ffilename) {
    if (*ffilename == 'M') minimal = true, ffilename++;
    switch (*ffilename) {
    case 'l':
      format = fmtLoLA; break;
    case 'p':
      format = fmtPEP; break;
    case 'r':
      format = fmtPROD; break;
    case 'm': case 0:
      break;
    default:
      thePrinter.printRaw ("unknown output format ");
      thePrinter.print (char_t (*ffilename));
      thePrinter.printRaw (", using default");
      thePrinter.finish ();
    }
    if (*ffilename && *++ffilename) {
      if (format == fmtPROD) {
	const char* suffix;
	if (!(suffix = strrchr (ffilename, '.')) ||
	    strcmp (suffix, ".net")) {
	  thePrinter.printRaw ("PROD model name ");
	  thePrinter.printQuoted (ffilename);
	  thePrinter.printRaw (" does not end in \".net\"");
	  thePrinter.finish ();
	  return;
	}
	static const char tbl[] = ".src/tables.c";
	char* tablename = new char[suffix - ffilename + sizeof tbl];
	memcpy (tablename, ffilename, suffix - ffilename);
	memcpy (tablename + (suffix - ffilename), tbl, sizeof tbl);
	if (!(f2 = fopen (tablename, "w"))) {
	  if (errno != ENOENT) {
	    delete[] tablename;
	    perror ("fopen");
	    return;
	  }
	  tablename[suffix - ffilename + 4] = 0;
	  if (mkdir (tablename, 0777)) {
	    delete[] tablename;
	    perror ("mkdir");
	    return;
	  }
	  tablename[suffix - ffilename + 4] = '/';
	  if (!(f2 = fopen (tablename, "w"))) {
	    delete[] tablename;
	    perror ("fopen");
	    return;
	  }
	}
      }

      if (!(f = fopen (ffilename, "w"))) {
	perror ("fopen");
	if (f2 != stdout)
	  fclose (f2);
	return;
      }
    }
  }

  if (verbose)
    fprintf (stderr, "%s:unfolding\n", netname);

  class LNet lnet (*net);
  if (minimal
      ? lnet.generateMinimal (thePrinter, maxerrors)
      : lnet.generate (thePrinter, maxerrors)) {
    unsigned* marking = lnet.unfold (*net->getInitMarking ());
    if (!marking) {
      thePrinter.printRaw ("assuming empty initial marking");
      thePrinter.finish ();
    }
    switch (format) {
    case fmtLoLA:
      lnet.toLoLA (f, marking);
      break;
    case fmtPEP:
      lnet.toPEP (f, marking);
      break;
    case fmtPROD:
      lnet.toPROD (f, f2, marking);
      break;
    case fmtDefault:
      {
	FILE* oldf = thePrinter.getOutput ();
	thePrinter.setOutput (f);
	lnet.display (thePrinter, marking);
	thePrinter.setOutput (oldf);
      }
    }
    delete[] marking;
  }
  else {
    thePrinter.printRaw ("unable to unfold the model");
    thePrinter.finish ();
  }
  if (f != stdout)
    fclose (f);
  if (f2 != stdout)
    fclose (f2);
}

void
dump (card_t visual)
{
  if (!subnet) {
    if (!notloaded) {
      notloaded = true;
      thePrinter.printRaw ("no model has been loaded");
      thePrinter.finish ();
    }
  }
  else if (class Dotty* d = visual ? Dotty::startVisual () : 0)
    d->dump (*subnet, visual > 1);
  else {
    thePrinter.setBOL (0);
    subnet->display (thePrinter);
    thePrinter.setBOL (&demsg);
  }
}

void
dumpgraph (card_t visual)
{
  if (!isState (0))
    return;
  if (class Dotty* d = visual ? Dotty::startVisual () : 0) {
    d->dumpgraph (graphreporter->getChild (modulepath), visual > 1);
    return;
  }
  thePrinter.setBOL (0);

  class GraphReporter& reporter = graphreporter->getChild (modulepath);
  const class Graph& graph = reporter.getGraph ();
  assert (&graph.getNet () == subnet);
  
  for (card_t state = 0; state < graph.getNumStates (); state++) {
    word_t* data;
    card_t* s = graph.getSuccessors (state, &data);
    assert (!s || *s);
    {
      class GlobalMarking* m = graph.fetchState (state);
      reporter.printState (*m, state, thePrinter);
      thePrinter.delimiter (':');
      if (!s)
	thePrinter.printRaw (graph.isProcessed (state)
			     ? "deadlock " : "unprocessed ");
      m->display (thePrinter);
      thePrinter.finish ();
      delete m;
    }
    if (card_t numPred = graph.getNumPredecessors (state)) {
      thePrinter.print (numPred);
      thePrinter.printRaw (numPred == 1
			   ? " predecessor"
			   : " predecessors");
      thePrinter.finish ();
    }
    if (s) {
      thePrinter.print (*s);
      thePrinter.printRaw (*s == 1
			   ? " successor"
			   : " successors");
      thePrinter.finish ();
      class BitUnpacker buf (data);
      for (card_t i = 1; i <= *s; i++) {
	const class Transition* transition;
	class Valuation v;
	subnet->decode (buf, transition, v, reporter.isFlattened ());
	thePrinter.printRaw ("transition ");
	thePrinter.print (transition->getName ());
	thePrinter.printRaw ("->");
	thePrinter.printState (s[i]);
	thePrinter.finish ();
	v.display (thePrinter);
	thePrinter.finish ();
      }
      delete[] data;
      delete[] s;
    }
  }
  thePrinter.setBOL (&demsg);
}

void
analyze (card_t state, bool breadth)
{
  thePrinter.setBOL (0);
  if (!state && !graphreporter) {
    safetyP (breadth, 0, 0);
    return;
  }
  if (!isState (state)) {
    thePrinter.setBOL (&demsg);
    return;
  }
  class GraphReporter& reporter = graphreporter->getChild (modulepath);
  const class Graph& graph = reporter.getGraph ();
  if (graph.isProcessed (state)) {
    thePrinter.printRaw ("state ");
    thePrinter.printState (state);
    thePrinter.printRaw (" has already been analyzed");
    thePrinter.finish ();
    return;
  }
  if (verbose)
    fprintf (stderr, "%s:generating the reachability graph\n", netname);
  reporter.init (state);
  reporter.analyze (breadth
		    ? StateReporter::Breadth
		    : StateReporter::Depth);
  stats ();
}

/** Open a file
 * @param name		name of the file
 * @param origfile	name of the original file
 * @param includes	include directories
 * @return		handle to the file, or 0 in case of error
 */
inline static FILE*
openfile (const char* name,
	  const char* origfile,
	  const class NameList& includes)
{
  FILE* f;
  // first try from the current directory
  if (!(f = fopen (name, "r"))) {
    // next, try from the directory of the original net

    size_t dirlength;
    char* fullname;
    size_t namelength = strlen (name);
    for (dirlength = 0; origfile[dirlength]; dirlength++);
    while (dirlength > 0 && origfile[dirlength] != '/')
      dirlength--;
    if (dirlength > 0) {
      fullname = new char[namelength + dirlength + 2];
      memcpy (fullname, origfile, dirlength + 1);
      memcpy (fullname + dirlength + 1, name, namelength + 1);
      f = fopen (fullname, "r");
      delete[] fullname;
      if (f)
	return f;
    }

    for (NameList::const_iterator i = includes.begin ();
	 i != includes.end (); i++) {
      const char* dirname = *i;
      dirlength = strlen (dirname);
      fullname = new char[namelength + dirlength + 2];
      memcpy (fullname, dirname, dirlength);
      fullname[dirlength] = '/';
      memcpy (fullname + dirlength + 1, name, namelength + 1);
      f = fopen (fullname, "r");
      delete[] fullname;
      if (f)
	break;
    }
  }

  if (f) {
    register int ch = getc (f);
    if (ferror (f)) {
      fclose (f);
      f = 0;
    }
    else
      ungetc (ch, f);
  }

  return f;
}

FILE*
openfile (const char* name)
{
  return openfile (name, netname, includePath);
}

FILE*
openscript (const char* name)
{
  return openfile (name, scriptname, includePath);
}

bool
isGraphless ()
{
  return net && !graphreporter;
}

bool
isState (card_t state)
{
  if (!graphreporter) {
    thePrinter.printRaw ("no reachability graph");
    if (!subnet)
      thePrinter.printRaw (" (no model loaded)");
    thePrinter.finish ();
    return false;
  }
  const class Graph& graph = graphreporter->getChild (modulepath).getGraph ();
  if (state < graph.getNumStates ())
    return true;
  thePrinter.printRaw ("the graph only has ");
  thePrinter.print (card_t (graph.getNumStates ()));
  thePrinter.printRaw (" states");
  thePrinter.finish ();
  return false;
}

void
show (card_t state, card_t visual)
{
  class GraphReporter& reporter = graphreporter->getChild (modulepath);
  if (!isState (state))
    return;
  if (class Dotty* d = visual ? Dotty::startVisual () : 0) {
    d->show (reporter, state, visual > 1);
    return;
  }
  thePrinter.setBOL (0);
  if (const class ComponentGraph* cgraph = reporter.getComponents ()) {
    card_t comp = cgraph->getComponent (state);
    if (comp != CARD_T_MAX) {
      thePrinter.printComponent (comp);
      thePrinter.printRaw (":");
    }
  }
  card_t numSucc = 0;
  const class Graph& graph = reporter.getGraph ();
  class GlobalMarking* m = graph.fetchState (state);
  {
    card_t* s = graph.getSuccessors (state);
    assert (!s || *s);
    if (s) {
      numSucc = *s;
      delete[] s;
    }
  }
  reporter.printState (*m, state, thePrinter);
  thePrinter.delimiter (':');
  if (!numSucc)
    thePrinter.printRaw (graph.isProcessed (state)
			 ? "deadlock " : "unprocessed ");
  m->display (thePrinter);
  thePrinter.finish ();
  if (card_t numPred = graph.getNumPredecessors (state)) {
    thePrinter.print (numPred);
    thePrinter.printRaw (numPred == 1
			 ? " predecessor"
			 : " predecessors");
    thePrinter.finish ();
  }
  if (numSucc) {
    thePrinter.print (numSucc);
    thePrinter.printRaw (numSucc == 1
			 ? " successor"
			 : " successors");
    thePrinter.finish ();
  }
  delete m;
  thePrinter.setBOL (&demsg);
}

void
show (const card_t* states, card_t visual)
{
  assert (states && *states);
  if (*states == 1)
    show (states[1], visual);
  else if (class Dotty* d = visual ? Dotty::startVisual () : 0)
    d->show (graphreporter->getChild (modulepath), states, visual > 1, true);
  else {
    thePrinter.setBOL (0);
    thePrinter.printRaw ("path  (");
    thePrinter.print (*states);
    thePrinter.printRaw (" nodes):");
    thePrinter.linebreak ();
    printReversePath (states);
    thePrinter.finish ();
    thePrinter.setBOL (&demsg);
  }
}

void
succ (card_t state, bool seq, card_t visual)
{
  if (!isState (state))
    return;
  class GraphReporter& reporter = graphreporter->getChild (modulepath);
  if (class Dotty* d = visual ? Dotty::startVisual () : 0) {
    d->succ (reporter, state, seq, visual > 1);
    return;
  }
  /** states that have been visited (for loop detection when seq==true) */
  std::set<card_t> visited;
 next:
  thePrinter.setBOL (0);
  reporter.getSuccessors (state).clear ();
  word_t* data;
  card_t* s = reporter.getGraph ().getSuccessors (state, &data);
  assert (!s || *s);
  if (s) {
    class BitUnpacker buf (data);
    for (card_t i = 1; i <= *s; i++) {
      const class Transition* transition;
      class Valuation v;
      subnet->decode (buf, transition, v, reporter.isFlattened ());
      thePrinter.printRaw ("transition ");
      thePrinter.print (transition->getName ());
      thePrinter.printRaw ("->");
      thePrinter.printState (s[i]);
      thePrinter.finish ();
      v.display (thePrinter);
      thePrinter.finish ();
    }
    delete[] data;
    if (seq && *s == 1 && visited.insert (state = s[1]).second) {
      show (state, 0);
      delete[] s;
      goto next;
    }
    delete[] s;
  }
  thePrinter.setBOL (&demsg);
}

void
pred (card_t state, bool seq, card_t visual)
{
  if (!isState (state))
    return;
  class GraphReporter& reporter = graphreporter->getChild (modulepath);
  if (class Dotty* d = visual ? Dotty::startVisual () : 0) {
    d->pred (reporter, state, seq, visual > 1);
    return;
  }
  /** states that have been visited (for loop detection when seq==true) */
  std::set<card_t> visited;
 prev:
  thePrinter.setBOL (0);
  Graph::fpos_t pos = reporter.getGraph ().getPredecessors (state);
  card_t prev = CARD_T_MAX;
  for (card_t oldsource = CARD_T_MAX; pos != FPOS_NONE; ) {
    card_t source = reporter.getGraph ().getPredecessor (pos);
    if (oldsource == source)
      continue; /* several arcs from the same state */
    oldsource = source;
    word_t* data;
    card_t* s = reporter.getGraph ().getSuccessors (source, &data);
    assert (s && *s);
    if (s) {
      class BitUnpacker buf (data);
      for (card_t i = 1; i <= *s; i++) {
	const class Transition* transition;
	class Valuation v;
	subnet->decode (buf, transition, v, !modular);
	if (s[i] != state)
	  continue;
	thePrinter.printRaw ("transition ");
	thePrinter.print (transition->getName ());
	thePrinter.printRaw ("<-");
	thePrinter.printState (source);
	thePrinter.finish ();
	v.display (thePrinter);
	thePrinter.finish ();
	if (prev == CARD_T_MAX)
	  prev = source;
	else
	  seq = false;
      }
      delete[] data;
      delete[] s;
    }
  }

  if (seq && prev != CARD_T_MAX && visited.insert (state = prev).second) {
    show (state, 0);
    goto prev;
  }

  thePrinter.setBOL (&demsg);
}

void
stats (void)
{
  if (!isState (0))
    return;
  const class GraphReporter& reporter = graphreporter->getChild (modulepath);
  thePrinter.setBOL (0);
  thePrinter.printQuoted (graphreporter->getGraph ().getFilename ());
  if (modulepath) {
    const class Net* n = net;
    for (unsigned i = 1; i <= *modulepath; i++) {
      thePrinter.delimiter ('/');
      n = &n->getChild (modulepath[i]);
      if (n->getName ())
	thePrinter.print (n->getName ());
      else
	thePrinter.print (modulepath[i]);
    }
    assert (n == subnet);
  }
  thePrinter.printRaw (": ");
  if (size_t num = reporter.getGraph ().getNumStates ()) {
    thePrinter.print (card_t (num));
    if (reporter.getMinSize () <= reporter.getMaxSize ()) {
      thePrinter.printRaw (" states (");
      thePrinter.print (card_t (reporter.getMinSize ()));
      if (reporter.getMaxSize () != reporter.getMinSize ()) {
	thePrinter.printRaw ("..");
	thePrinter.print (card_t (reporter.getMaxSize ()));
      }
      thePrinter.printRaw (" bytes), ");
    }
    else
      thePrinter.printRaw (" states, ");
  }
  else
    thePrinter.printRaw ("no states, ");
  if (reporter.getNumLocal ()) {
    thePrinter.printRaw ("at most ");
    thePrinter.print (reporter.getNumLocal ());
    thePrinter.printRaw (" module states, ");
  }
  if (reporter.getNumErrors ()) {
    thePrinter.print (card_t (reporter.getNumErrors ()));
    thePrinter.printRaw (reporter.getNumErrors () == 1
			 ? " error, "
			 : " errors, ");
  }
  thePrinter.print (reporter.getGraph ().getNumArcs ());
  thePrinter.printRaw (" arcs");
  if (const class ComponentGraph* cgraph =
      graphreporter->getChild (modulepath).getComponents ()) {
    thePrinter.printRaw (", ");
    thePrinter.print (cgraph->size ());
    thePrinter.printRaw (cgraph->size () == 1
			 ? " component, "
			 : " components, ");
    thePrinter.print (cgraph->getNumTerminals ());
    thePrinter.printRaw (cgraph->getNumTerminals () == 1
			 ? " terminal component"
			 : " terminal components");
  }
  thePrinter.finish ();
  showEnabled (reporter.getEnabled ());
  thePrinter.setBOL (&demsg);
}

static void
showEnabled (const char* log)
{
  assert (!!subnet);
  if (log) {
    const unsigned numEnabled = subnet->getNumEnabled ();
    for (unsigned i = 0; i < numEnabled; i++) {
      if (!log[i]) {
	thePrinter.printRaw ("disabled enabledness sets:");
	thePrinter.print (i);
	while (++i < numEnabled)
	  if (!log[i])
	    thePrinter.delimiter (','), thePrinter.print (i);
	thePrinter.finish ();
	return;
      }
    }
  }
}

void
showtimes (void)
{
#ifndef unix
  clock_t nowclock = clock ();
  fprintf (stderr, "%f seconds\n",
	   double (nowclock - lastclock) / CLOCKS_PER_SEC);
  lastclock = nowclock;
#else // !unix
  static clock_t user, sys;
  struct tms current;
  clock_t currentclock = times (&current);
  if (currentclock == -1) {
    perror ("times");
    return;
  }

  long ticks = sysconf (_SC_CLK_TCK);
  double dt_real = double (currentclock - lastclock) / ticks;
  double dt_user = double (current.tms_utime - user) / ticks;
  double dt_sys = double (current.tms_stime - sys) / ticks;

  fprintf (stderr, "%f real, %f user, %f system\n",
	   dt_real, dt_user, dt_sys);
  user = current.tms_utime;
  sys = current.tms_stime;
  lastclock = currentclock;
#endif // !unix
}

void
strong (card_t state, class Expression* cond)
{
  if (!isState (state))
    return;
  unsigned comps = graphreporter->getChild (modulepath).strong (state, cond);
  if (!comps && !interrupted) {
    thePrinter.printRaw ("failed to compute strongly connected components");
    thePrinter.finish ();
  }
  else if (comps) stats ();
  cond->destroy ();
}

/** Check if a component number exists
 * @param comp	the component
 * @return	true if the strongly connected component exists
 */
static bool
isComponent (card_t comp)
{
  const class ComponentGraph* cgraph = graphreporter
    ? graphreporter->getChild (modulepath).getComponents ()
    : 0;
  if (!cgraph) {
    thePrinter.printRaw ("strongly connected components "
			 "have not been computed");
    thePrinter.finish ();
    return false;
  }
  if (comp < cgraph->size ())
    return true;
  thePrinter.printRaw ("there are only ");
  thePrinter.print (card_t (cgraph->size ()));
  thePrinter.printRaw (" strongly connected components");
  thePrinter.finish ();
  return false;
}

void
terminal (void)
{
  if (!isComponent (0))
    return;
  const class GraphReporter& reporter = graphreporter->getChild (modulepath);
  const class ComponentGraph* cgraph = reporter.getComponents ();
  thePrinter.setBOL (0);
  for (card_t comp = 0; comp < cgraph->size (); comp++) {
    if (cgraph->getNumSucc (comp))
      continue;
    card_t* states = cgraph->getStates (comp);
    assert (states && *states);
    assert (*states <= reporter.getGraph ().getNumStates ());
    if (*states > 1 && *states != reporter.getGraph ().getNumStates ()) {
      thePrinter.printRaw ("non-trivial terminal component ");
      thePrinter.printComponent (comp);
      thePrinter.printRaw (" (");
      thePrinter.print (*states);
      thePrinter.printRaw (" states):");
      thePrinter++.linebreak ();
      for (card_t state = 1;;) {
	thePrinter.printState (states[state]);
	if (state++ < *states)
	  thePrinter.delimiter (' ');
	else break;
      }
      thePrinter--.finish ();
    }
    delete[] states;
  }
  thePrinter.setBOL (&demsg);
}

void
allcomps (card_t visual)
{
  if (!isComponent (0))
    return;
  const class GraphReporter& reporter = graphreporter->getChild (modulepath);
  const class ComponentGraph* cgraph = reporter.getComponents ();
  if (class Dotty* d = visual ? Dotty::startVisual () : 0) {
    d->components (*cgraph, visual > 1);
    return;
  }
  thePrinter.setBOL (0);
  for (card_t comp = 0; comp < cgraph->size (); comp++) {
    card_t* states = cgraph->getStates (comp);
    assert (states && *states);
    assert (*states <= reporter.getGraph ().getNumStates ());
    if (*states > 1 && *states != reporter.getGraph ().getNumStates ())
      thePrinter.printRaw ("non-trivial ");
    if (!cgraph->getNumSucc (comp))
      thePrinter.printRaw ("terminal ");
    thePrinter.printRaw ("component ");
    thePrinter.printComponent (comp);
    if (*states == reporter.getGraph ().getNumStates ()) {
      thePrinter.printRaw (" (all states)");
      thePrinter.finish ();
      delete[] states;
      continue;
    }
    thePrinter.printRaw (" (");
    thePrinter.print (*states);
    thePrinter.printRaw (" states):");
    thePrinter++.linebreak ();
    for (card_t state = 1;;) {
      thePrinter.printState (states[state]);
      if (state++ < *states)
	thePrinter.delimiter (' ');
      else break;
    }
    thePrinter--.finish ();
    delete[] states;
  }
  thePrinter.setBOL (&demsg);
}

void
comp_show (card_t comp,
	   class Expression* cond,
	   card_t visual)
{
  if (!isComponent (comp)) {
    cond->destroy ();
    return;
  }
  const class GraphReporter& reporter = graphreporter->getChild (modulepath);
  if (class Dotty* d = visual ? Dotty::startVisual () : 0) {
    d->component (reporter, comp, cond, visual > 1);
    return;
  }
  const class ComponentGraph* cgraph = reporter.getComponents ();
  thePrinter.setBOL (0);
  card_t* states = cgraph->getStates (comp);
  assert (states && *states);
  assert (*states <= reporter.getGraph ().getNumStates ());
  if (!cgraph->getNumSucc (comp))
    thePrinter.printRaw ("terminal ");
  if (!cond) {
    if (*states == 1) {
      thePrinter.printRaw ("trivial strongly connected component ");
      thePrinter.printComponent (comp);
      thePrinter.printRaw (": ");
      thePrinter.printState (states[1]);
      thePrinter.finish ();
      delete[] states;
      return;
    }
    else if (*states == reporter.getGraph ().getNumStates ()) {
      thePrinter.printRaw ("trivial strongly connected component ");
      thePrinter.printComponent (comp);
      thePrinter.printRaw (": (all nodes)");
      thePrinter.finish ();
      delete[] states;
      return;
    }
  }
  thePrinter.printRaw ("strongly connected component ");
  thePrinter.printComponent (comp);
  thePrinter.printRaw (" (");
  thePrinter.print (*states);
  thePrinter.printRaw (" states):");
  thePrinter++.linebreak ();
  card_t state = 1;
  if (cond) {
    for (;;) {
      if (!reporter.getGraph ().eval (states[state], cond)) {
	if (state++ < *states) continue; else break;
      }
      thePrinter.printState (states[state]);
      if (state++ < *states)
	thePrinter.delimiter (' ');
      else break;
    }
  }
  else {
    for (;;) {
      thePrinter.printState (states[state]);
      if (state++ < *states)
	thePrinter.delimiter (' ');
      else break;
    }
  }
  delete[] states;
  thePrinter--.finish ();
  thePrinter.setBOL (&demsg);
  cond->destroy ();
}

void
comp_succ (card_t comp, card_t visual)
{
  if (!isComponent (comp))
    return;
  const class GraphReporter& reporter = graphreporter->getChild (modulepath);
  const class ComponentGraph* cgraph = reporter.getComponents ();
  if (class Dotty* d = visual ? Dotty::startVisual () : 0) {
    d->comp_succ (*cgraph, comp, visual > 1);
    return;
  }
  if (card_t* s = cgraph->getSucc (comp)) {
    assert (*s > 0);
    thePrinter.setBOL (0);
    for (comp = 1;;) {
      thePrinter.printComponent (s[comp]);
      if (comp++ < *s)
	thePrinter.delimiter (' ');
      else break;
    }
    delete[] s;
    thePrinter.finish ();
    thePrinter.setBOL (&demsg);
  }
}

void
comp_pred (card_t comp, card_t visual)
{
  if (!isComponent (comp))
    return;
  const class GraphReporter& reporter = graphreporter->getChild (modulepath);
  const class ComponentGraph* cgraph = reporter.getComponents ();
  if (class Dotty* d = visual ? Dotty::startVisual () : 0) {
    d->comp_pred (*cgraph, comp, visual > 1);
    return;
  }
  if (card_t* p = cgraph->getPred (comp)) {
    assert (*p > 0);
    thePrinter.setBOL (0);
    for (comp = 1;;) {
      thePrinter.printComponent (p[comp]);
      if (comp++ < *p)
	thePrinter.delimiter (' ');
      else break;
    }
    delete[] p;
    thePrinter.finish ();
    thePrinter.setBOL (&demsg);
  }
}

static void
printLSTS (const class Graph& graph,
	   const card_t* path,
	   bool reverse)
{
  class LSTS& l = *graph.getLSTS ();
  class BitVector visited (graph.getNumStates ());
  for (const card_t* p = path + *path; p > path; p--) {
    if (!visited.tset (*p)) {
      class GlobalMarking* m = graph.fetchState (*p);
      l.outputState (*m, *p);
      delete m;
    }
    if (const card_t* next = reverse
	? (p < path + *path ? p + 1 : 0)
	: (p > path + 1 ? p - 1 : 0)) {
      word_t* data;
      card_t* s = graph.getSuccessors (*p, &data);
      assert (!s || *s);
      if (s) {
	class BitUnpacker buf (data);
	for (card_t i = 1; i <= *s; i++) {
	  const class Transition* transition;
	  class Valuation v;
	  subnet->decode (buf, transition, v, !modular);
	  if (s[i] != *next)
	    continue;
	  l.outputArc (*p, *next, *transition, v);
	}
	delete[] data;
	delete[] s;
      }
    }
  }
}

static void
printPath (const card_t* path)
{
  assert (path && graphreporter);
  const class Graph& graph = graphreporter->getChild (modulepath).getGraph ();
  if (graph.getLSTS ()) {
    printLSTS (graph, path, false);
    const_cast<class Graph&>(graph).setLSTS (0);
  }
  for (const card_t* p = path + *path;;) {
    show (*p, false);
    if (--p == path)
      break;
    word_t* data;
    card_t* s = graph.getSuccessors (p[1], &data);
    assert (s && *s);
    if (s) {
      class BitUnpacker buf (data);
      for (card_t i = 1; i <= *s; i++) {
	const class Transition* transition;
	class Valuation v;
	subnet->decode (buf, transition, v, !modular);
	if (s[i] != *p)
	  continue;
	thePrinter.setBOL (0);
	thePrinter.printRaw ("transition ");
	thePrinter.print (transition->getName ());
	thePrinter.printRaw ("->");
	thePrinter.printState (s[i]);
	thePrinter.finish ();
	v.display (thePrinter);
	thePrinter.finish ();
      }
      delete[] data;
      delete[] s;
    }
  }
}

static void
printReversePath (const card_t* path)
{
  assert (path && graphreporter);
  const class Graph& graph = graphreporter->getChild (modulepath).getGraph ();
  if (graph.getLSTS ()) {
    printLSTS (graph, path, true);
    const_cast<class Graph&>(graph).setLSTS (0);
  }
  for (card_t j = 1;;) {
    show (path[j], false);
    if (j++ == *path)
      break;
    word_t* data;
    card_t* s = graph.getSuccessors (path[j - 1], &data);
    assert (!s || *s);
    if (s) {
      class BitUnpacker buf (data);
      for (card_t i = 1; i <= *s; i++) {
	const class Transition* transition;
	class Valuation v;
	subnet->decode (buf, transition, v, !modular);
	if (s[i] != path[j])
	  continue;
	thePrinter.setBOL (0);
	thePrinter.printRaw ("transition ");
	thePrinter.print (transition->getName ());
	thePrinter.printRaw ("->");
	thePrinter.printState (s[i]);
	thePrinter.finish ();
	v.display (thePrinter);
	thePrinter.finish ();
      }
      delete[] data;
      delete[] s;
    }
  }
}

void
path_sc (card_t state, card_t comp,
	 class Expression* pathc,
	 card_t visual)
{
  if (!isState (state) || !isComponent (comp)) {
    pathc->destroy ();
    return;
  }
  thePrinter.setBOL (0);
  const class GraphReporter& reporter = graphreporter->getChild (modulepath);
  const class ComponentGraph* cgraph = reporter.getComponents ();
  if (card_t* path = cgraph->path (state, comp, pathc)) {
    if (class Dotty* d = visual ? Dotty::startVisual () : 0)
      d->show (reporter, path, visual > 1, false);
    else {
      thePrinter.printRaw ("shortest path from ");
      thePrinter.printState (state);
      thePrinter.printRaw (" to ");
      thePrinter.printComponent (comp);
      thePrinter.printRaw (" (");
      thePrinter.print (*path);
      thePrinter.printRaw (" nodes):");
      thePrinter.linebreak ();
      printPath (path);
      thePrinter.finish ();
    }
    delete[] path;
  }
  else {
    thePrinter.printRaw (pathc ? "no such path from " : "no path from ");
    thePrinter.printState (state);
    thePrinter.printRaw (" to ");
    thePrinter.printComponent (comp);
    thePrinter.finish ();
  }
  thePrinter.setBOL (&demsg);
  pathc->destroy ();
}

void
path_cs (card_t comp, card_t state,
	 class Expression* pathc,
	 card_t visual)
{
  if (!isState (state) || !isComponent (comp)) {
    pathc->destroy ();
    return;
  }
  const class GraphReporter& reporter = graphreporter->getChild (modulepath);
  const class ComponentGraph* cgraph = reporter.getComponents ();
  thePrinter.setBOL (0);
  if (card_t* path = cgraph->rpath (state, comp, pathc)) {
    if (class Dotty* d = visual ? Dotty::startVisual () : 0)
      d->show (reporter, path, visual > 1, true);
    else {
      thePrinter.printRaw ("shortest path from ");
      thePrinter.printComponent (comp);
      thePrinter.printRaw (" to ");
      thePrinter.printState (state);
      thePrinter.printRaw (" (");
      thePrinter.print (*path);
      thePrinter.printRaw (" nodes):");
      thePrinter.linebreak ();
      printReversePath (path);
      thePrinter.finish ();
    }
    delete[] path;
  }
  else {
    thePrinter.printRaw (pathc ? "no such path from " : "no path from ");
    thePrinter.printComponent (comp);
    thePrinter.printRaw (" to ");
    thePrinter.printState (state);
    thePrinter.finish ();
  }
  thePrinter.setBOL (&demsg);
  pathc->destroy ();
}

void
path_ss (card_t state, card_t target,
	 class Expression* pathc,
	 card_t visual)
{
  if (!isState (state) || !isState (target)) {
    pathc->destroy ();
    return;
  }
  const class GraphReporter& reporter = graphreporter->getChild (modulepath);
  thePrinter.setBOL (0);
  if (card_t* path = reporter.getGraph ().path (state, target, pathc)) {
    if (class Dotty* d = visual ? Dotty::startVisual () : 0)
      d->show (reporter, path, visual > 1, false);
    else {
      thePrinter.printRaw ("shortest path from ");
      thePrinter.printState (state);
      thePrinter.printRaw (" to ");
      thePrinter.printState (target);
      thePrinter.printRaw (" (");
      thePrinter.print (*path);
      thePrinter.printRaw (" nodes):");
      thePrinter.linebreak ();
      printPath (path);
      thePrinter.finish ();
    }
    delete[] path;
  }
  else {
    thePrinter.printRaw (pathc ? "no such path from " : "no path from ");
    thePrinter.printState (state);
    thePrinter.printRaw (" to ");
    thePrinter.printState (target);
    thePrinter.finish ();
  }
  thePrinter.setBOL (&demsg);
  pathc->destroy ();
}

void
path_se (card_t state, class Expression& cond,
	 class Expression* pathc,
	 card_t visual)
{
  if (!isState (state)) {
    cond.destroy ();
    pathc->destroy ();
    return;
  }
  const class GraphReporter& reporter = graphreporter->getChild (modulepath);
  thePrinter.setBOL (0);
  if (card_t* path = reporter.getGraph ().path (state, cond, pathc)) {
    if (class Dotty* d = visual ? Dotty::startVisual () : 0)
      d->show (reporter, path, visual > 1, false);
    else {
      thePrinter.printRaw ("shortest path from ");
      thePrinter.printState (state);
      thePrinter.printRaw (" to condition (");
      thePrinter.print (*path);
      thePrinter.printRaw (" nodes):");
      thePrinter.linebreak ();
      printPath (path);
      thePrinter.finish ();
    }
    delete[] path;
  }
  else {
    thePrinter.printRaw (pathc ? "no such path from " : "no path from ");
    thePrinter.printState (state);
    thePrinter.printRaw (" to condition");
    thePrinter.finish ();
  }
  thePrinter.setBOL (&demsg);
  cond.destroy ();
  pathc->destroy ();
}

void
rpath_se (card_t state, class Expression& cond,
	  class Expression* pathc,
	  card_t visual)
{
  if (!isState (state)) {
    cond.destroy ();
    pathc->destroy ();
    return;
  }
  const class GraphReporter& reporter = graphreporter->getChild (modulepath);
  thePrinter.setBOL (0);
  if (card_t* path = reporter.getGraph ().rpath (state, cond, pathc)) {
    if (class Dotty* d = visual ? Dotty::startVisual () : 0)
      d->show (reporter, path, visual > 1, true);
    else {
      thePrinter.printRaw ("shortest path from condition to ");
      thePrinter.printState (state);
      thePrinter.printRaw (" (");
      thePrinter.print (*path);
      thePrinter.printRaw (" nodes):");
      thePrinter.linebreak ();
      printReversePath (path);
      thePrinter.finish ();
    }
    delete[] path;
  }
  else {
    thePrinter.printRaw (pathc
			 ? "no such path from condition to "
			 : "no path from condition to ");
    thePrinter.printState (state);
    thePrinter.finish ();
  }
  thePrinter.setBOL (&demsg);
  cond.destroy ();
  pathc->destroy ();
}

void
path_sl (card_t state,
	 const card_t* loop,
	 class Expression* pathc,
	 card_t visual)
{
  if (!isState (state)) {
    pathc->destroy ();
    return;
  }
  thePrinter.setBOL (0);
  const class GraphReporter& reporter = graphreporter->getChild (modulepath);
  if (card_t* path = reporter.getGraph ().path (state, loop, pathc)) {
    if (class Dotty* d = visual ? Dotty::startVisual () : 0)
      d->show (reporter, path, visual > 1, false);
    else {
      thePrinter.printRaw ("shortest path from ");
      thePrinter.printState (state);
      thePrinter.printRaw (" to loop (");
      thePrinter.print (*path);
      thePrinter.printRaw (" nodes):");
      thePrinter.linebreak ();
      printPath (path);
      thePrinter.finish ();
    }
    delete[] path;
  }
  else {
    thePrinter.printRaw (pathc
			 ? "no such path from "
			 : "no path from ");
    thePrinter.printState (state);
    thePrinter.printRaw (" to loop");
    thePrinter.finish ();
  }
  thePrinter.setBOL (&demsg);
  pathc->destroy ();
}

void
eval (card_t state, class Expression* expr, card_t visual)
{
  thePrinter.setBOL (0);
  if (!expr);
  else if (expr->isTemporal ()) {
    thePrinter.setBOL (&demsg);
    onthefly (state, expr, visual);
  }
  else if (!isState (state));
  else {
    class Valuation v;
    class GlobalMarking* m =
      graphreporter->getChild (modulepath).getGraph ().fetchState (state);
    v.setGlobalMarking (m);
    if (expr->isSet ()) {
      if (class PlaceMarking* p = expr->meval (v)) {
	p->display (thePrinter);
	delete p;
      }
      else
	v.display (thePrinter);
    }
    else {
      if (class Value* value = expr->eval (v)) {
	value->display (thePrinter);
	delete value;
      }
      else
	v.display (thePrinter);
    }
    thePrinter.finish ();
    delete m;
  }
  thePrinter.setBOL (&demsg);
}

void
fireTransition (class Transition& trans, card_t state, card_t visual)
{
  if (!isState (state))
    return;
  if (!trans.isUnifiable (Transition::uNormal, thePrinter))
    return;
  if (!graphreporter->isFlattened ()) {
    thePrinter.printRaw ("no anonymous transitions for modular state spaces");
    thePrinter.finish ();
    return;
  }
  class GraphReporter& reporter = graphreporter->getChild (modulepath);
  reporter.init (state);
  class SearchList& states = reporter.getSuccessors (trans);
  for (SearchList::const_iterator i = states.begin (); i != states.end (); i++)
    show (*i, visual);
  states.clear ();
}
