/*
 * Scyther : An automatic verifier for security protocols.
 * Copyright (C) 2007 Cas Cremers
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

/**
 * @file system.c 
 * \brief system related logic.
 */
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include "term.h"
#include "termlist.h"
#include "knowledge.h"
#include "system.h"
#include "debug.h"
#include "error.h"
#include "role.h"
#include "mgu.h"
#include "switches.h"
#include "binding.h"
#include "depend.h"
#include "specialterm.h"

//! Global count of protocols
int protocolCount;

//! External
extern Protocol INTRUDER;

//! Switch for indent or not.
static int indentState = 0;
//! Current indent depth.
static int indentDepth = 0;

//! Initialise a system structure.
/**
 *@return A system structure pointer with initial values.
 */
System
systemInit ()
{
  System sys = (System) malloc (sizeof (struct system));

  /* initially, no trace ofcourse */
  sys->step = 0;
  sys->shortestattack = INT_MAX;
  sys->maxtracelength = INT_MAX;

  /* init rundefs */
  sys->maxruns = 0;
  sys->runs = NULL;
  /* no protocols yet */
  protocolCount = 0;
  sys->protocols = NULL;
  sys->locals = NULL;
  sys->variables = NULL;
  sys->agentnames = NULL;
  sys->untrusted = NULL;
  sys->globalconstants = NULL;
  sys->hidden = NULL;
  sys->secrets = NULL;		// list of claimed secrets
  sys->synchronising_labels = NULL;
  /* no protocols => no protocol preprocessed */
  sys->rolecount = 0;
  sys->roleeventmax = 0;
  sys->claimlist = NULL;
  sys->labellist = NULL;
  sys->knowledgedefined = false;	// currently, we have backwards compatibility for empty role knowledge defs (disabling well-formedness rules)
  sys->attackid = 0;		// First attack will have id 1, because the counter is increased before any attacks are displayed.

  /* arachne assist */
  bindingInit (sys);
  sys->bindings = NULL;
  sys->current_claim = NULL;
  sys->trustedRoles = NULL;
  sys->hasUntypedVariable = false;

  /* reset global counters */
  systemReset (sys);

  return sys;
}

//! Reset a system state after some exploration.
/**
 *@param sys A system structure pointer.
 *@return Counter values have been reset.
 */
void
systemReset (const System sys)
{
  Claimlist cl;

  /* some initial counters */
  sys->states = statesIncrease (STATES0);	//!< Initial state is not explored, so start counting at 1
  sys->interval = sys->states;	//!< To keep in line with the states
  sys->claims = STATES0;
  sys->failed = STATES0;
  sys->explore = 1;		// do explore the space
  cl = sys->claimlist;
  while (cl != NULL)
    {
      cl->count = STATES0;
      cl->failed = STATES0;
      cl = cl->next;
    }

  termlistDestroy (sys->secrets);	// remove old secrets list
  sys->secrets = NULL;		// list of claimed secrets
  sys->num_intruder_runs = 0;	// number of intruder runs
  sys->num_regular_runs = 0;	// number of regular runs

  /* transfer switches */
  sys->maxtracelength = switches.maxtracelength;
}

//! Initialize runtime system (according to cut traces, limited runs)
void
systemRuns (const System sys)
{
  int run;

  sys->lastChooseRun = -1;
  for (run = 0; run < sys->maxruns; run++)
    {
      Roledef rd;

      rd = runPointerGet (sys, run);
      if (rd != NULL && rd->internal && rd->type == READ)
	{
	  /* increasing run traversal, so this yields max */
	  sys->lastChooseRun = run;
	}
    }
}

//! Delete a system structure and clear used memory for all buffers.
/**
 * Is more thorough than systemDestroy().
 *\sa systemDestroy()
 */
void
systemDone (const System sys)
{
  int s;

  /* clear globals, which were defined in systemStart */

  s = sys->maxtracelength + 1;
  free (sys->traceEvent);
  free (sys->traceRun);
  free (sys->traceKnow);
  free (sys->traceNode);

  /* clear roledefs */
  while (sys->maxruns > 0)
    {
      roleInstanceDestroy (sys);
    }

  /* undo bindings (for arachne) */

  bindingDone ();

  /* clear substructures */
  termlistDestroy (sys->secrets);

  /* clear main system */
  systemDestroy (sys);
}

//! Destroy a system memory block and system::runs
/**
 * Ignores any other substructes.
 *\sa systemDone()
 */
void
systemDestroy (const System sys)
{
  free (sys->runs);
  free (sys);
}

//! Ensures that a run can be added to the system.
/**
 * Allocates memory to allow a run to be added, if needed.
 * This is meant to be used before using runPointerSet().
 */

void
ensureValidRun (const System sys, int run)
{
  int i, oldsize;

  if (run < sys->maxruns)
    return;

  /* this amount of memory was not allocated yet */
  /* (re)allocate  space */
  /* Note, this is never explicitly freed, because it is never
     copied */
  /* update size parameter */
  oldsize = sys->maxruns;
  sys->maxruns = run + 1;
  sys->runs = (Run) realloc (sys->runs, sizeof (struct run) * (sys->maxruns));

  /* create runs, set the new pointer(s) to NULL */
  for (i = oldsize; i < sys->maxruns; i++)
    {
      /* init run */
      sys->runs[i].protocol = NULL;
      sys->runs[i].role = NULL;
      sys->runs[i].step = 0;
      sys->runs[i].rolelength = 0;

      sys->runs[i].index = NULL;
      sys->runs[i].start = NULL;
      sys->runs[i].know = NULL;

      sys->runs[i].rho = NULL;
      sys->runs[i].sigma = NULL;
      sys->runs[i].constants = NULL;

      sys->runs[i].locals = NULL;
      sys->runs[i].artefacts = NULL;
      sys->runs[i].substitutions = NULL;


      sys->runs[i].prevSymmRun = -1;
      sys->runs[i].firstNonAgentRead = -1;
      sys->runs[i].firstReal = 0;
    }
}

//! Print a run.
void
runPrint (Roledef rd)
{
  int i;

  indent ();
  i = 0;
  while (rd != NULL)
    {
      eprintf ("%i: ", i);
      roledefPrint (rd);
      eprintf ("\n");
      i++;
      rd = rd->next;
    }
}

//! Print all runs in the system structure.
void
runsPrint (const System sys)
{
  int i;

  indent ();
  eprintf ("[ Run definitions ]\n");
  for (i = 0; i < (sys->maxruns); i++)
    {
      indent ();
      eprintf ("Run definition %i:\n", i);
      runPrint (runPointerGet (sys, i));
      eprintf ("\n");
    }
}

//! Determine whether a term is sent or claimed, but not read first in a roledef
/**
 * @returns True iff the term occurs, and is sent/claimed first. If this returns true,
 * we have to prefix a read.
 */
int
not_read_first (const Roledef rdstart, const Term t)
{
  Roledef rd;

  rd = rdstart;
  while (rd != NULL)
    {
      if (termSubTerm (rd->message, t))
	{
	  return (rd->type != READ);
	}
      rd = rd->next;
    }
  /* this term is not read or sent explicitly, which is no problem */
  /* So we signal we don't have to prefix a read */
  return 0;
}

//! Yield the agent name term in a role, for a run in the system.
/**
 *@param sys The system.
 *@param run The run in which we are interested.
 *@param role The role of which we want to know the agent.
 */
Term
agentOfRunRole (const System sys, const int run, const Term role)
{
  Termlist agents;

  // Agent variables have the same symbol as the role names, so
  // we can scan for this.
  agents = sys->runs[run].rho;
  while (agents != NULL)
    {
      Term agent;

      agent = agents->term;
      if (TermSymb (role) == TermSymb (agent))
	{
	  return agent;
	}
      else
	{
	  agents = agents->next;
	}
    }
  return NULL;
}

//! Yield the actor agent of a run in the system.
/**
 *@param sys The system.
 *@param run The run in which we are interested.
 */
Term
agentOfRun (const System sys, const int run)
{
  return agentOfRunRole (sys, run, sys->runs[run].role->nameterm);
}

//! Determine first read with variables besides agents
/**
 *@todo For now, we assume it is simply the first read after the choose, if there is one.
 */
int
firstNonAgentRead (const System sys, int rid)
{
  int step;
  Roledef rd;

  if (sys->runs[rid].prevSymmRun == -1)
    {
      /* if there is no symmetrical run, then this doesn't apply at all */
      return -1;
    }
  rd = sys->runs[rid].start;
  step = 0;
  while (rd != NULL && rd->internal && rd->type == READ)	// assumes lazy LR eval
    {
      rd = rd->next;
      step++;
    }
  if (rd != NULL && !rd->internal && rd->type == READ)	// assumes lazy LR eval
    {
#ifdef DEBUG
      warning
	("First read %i with dependency on symmetrical found in run %i.",
	 step, rid);
#endif
      return step;
    }
  /* no such read */
  return -1;
}


/*************************************************
 *
 * Support code for roleInstance
 *
*************************************************/

//! Prefix a read before a given run.
/**
 * Maybe this simply needs integration in the role definitions. However, in practice it
 * depends on the specific scenario. For Arachne it can thus depend on the roledef.
 *
 * Stores the (new) rd pointer in start and index
 */
void
run_prefix_read (const System sys, const int run, Roledef rd,
		 const Term extterm)
{
  /* prefix a read for such reads. TODO: this should also cover any external stuff */
  if (extterm != NULL)
    {
      Roledef rdnew;

      rdnew = roledefInit (READ, NULL, NULL, NULL, extterm, NULL);
      /* this is an internal action! */
      rdnew->internal = 1;
      /* Store this new pointer */
      rdnew->next = rd;
      rd = rdnew;
      /* mark the first real action */
      sys->runs[run].firstReal++;
    }
  /* possibly shifted rd */
  sys->runs[run].start = rd;
  sys->runs[run].index = rd;
}


//! Localize run
/**
 * Takes a run roledef list and substitutes fromlist into tolist terms.
 * Furthermore, localizes all substitutions occurring in this, which termLocal
 * does not. Any localized substitutions are stored as well in a list.
 */
void
run_localize (const System sys, const int rid, Termlist fromlist,
	      Termlist tolist, Termlist substlist)
{
  Roledef rd;

  rd = sys->runs[rid].start;
  while (rd != NULL)
    {
      rd->from = termLocal (rd->from, fromlist, tolist);
      rd->to = termLocal (rd->to, fromlist, tolist);
      rd->message = termLocal (rd->message, fromlist, tolist);
      rd = rd->next;
    }

  // Substlist is NULL currently? No usage of this last stuff now
  // TODO
  if (substlist != NULL)
    {
      error ("Substlist should be NULL in run_localize");
    }
  sys->runs[rid].substitutions = NULL;
  while (substlist != NULL)
    {
      Term t;

      t = substlist->term;
      if (t->subst != NULL)
	{
	  t->subst = termLocal (t->subst, fromlist, tolist);
	  sys->runs[rid].substitutions =
	    termlistAdd (sys->runs[rid].substitutions, t);
	}
      substlist = substlist->next;
    }
}


//! Instantiate a role by making a new run for Arachne
/**
 * This involves creation of a new run(id).
 * Copy & subst of Roledef, Agent knowledge.
 * Tolist might contain type constants.
*/

void
roleInstanceArachne (const System sys, const Protocol protocol,
		     const Role role, const Termlist paramlist,
		     Termlist substlist)
{
  int rid;
  Run runs;
  Roledef rd;
  Termlist fromlist = NULL;	// deleted at the end
  Termlist tolist = NULL;	// -> .locals
  Term extterm = NULL;		// construction thing (will go to artefacts)

  void createLocal (Term oldt, int isvariable, int isrole)
  {
    Term newt;

    // Create new term with the same symbol
    if (isvariable)
      {
	// Force variable
	newt = makeTermType (VARIABLE, TermSymb (oldt), rid);
      }
    else
      {
	// Force local (weirdly enough called global)
	newt = makeTermType (GLOBAL, TermSymb (oldt), rid);
      }
    newt->stype = oldt->stype;	// copy list of types
    newt->roleVar = isrole;	// set role status

    // Add to copy list
    TERMLISTADD (fromlist, oldt);
    TERMLISTADD (tolist, newt);

    // Add to registration lists
    // Everything to destructor list
    TERMLISTADD (runs[rid].artefacts, newt);
    // Variable / Constant?
    if (isvariable)
      {
	TERMLISTADD (sys->variables, newt);
	if (isrole)
	  {
	    // role variable
	    /*
	     * We use append to make sure the order is
	     * consistent with the role names list.
	     */
	    TERMLISTAPPEND (runs[rid].rho, newt);
	    if (!role->initiator)
	      {
		// For non-initiators, we prepend the reading of the role names

		// XXX disabled for now TODO [x] [cc]
		if (0 == 1 && not_read_first (rd, oldt))
		  {
		    /* this term is forced as a choose, or it does not occur in the (first) read event */
		    if (extterm == NULL)
		      {
			extterm = newt;
		      }
		    else
		      {
			extterm = makeTermTuple (newt, extterm);
			// NOTE: don't these get double deleted? By roledefdestroy?
			TERMLISTAPPEND (runs[rid].artefacts, extterm);
		      }
		  }
	      }
	  }
	else
	  {
	    // normal variable
	    TERMLISTAPPEND (runs[rid].sigma, newt);
	  }
      }
    else
      {
	// local constant
	TERMLISTADD (runs[rid].constants, newt);
      }
  }

  //! The next function makes locals for all in the list. Flags denote whether it is a variable or role.
  void createLocals (Termlist list, int isvariable, int isrole)
  {
    while (list != NULL)
      {
	createLocal (list->term, isvariable, isrole);
	list = list->next;
      }
  }

  /* claim runid, allocate space */
  rid = sys->maxruns;
  ensureValidRun (sys, rid);	// creates a new block
  runs = sys->runs;		// simple structure pointer transfer (shortcut)

  /* duplicate roledef in buffer rd */
  /* Notice that it is not stored (yet) in the run structure,
   * and that termDuplicate is used internally
   */
  rd = roledefDuplicate (role->roledef);

  /* set parameters */
  /* generic setup of inherited stuff */
  runs[rid].protocol = protocol;
  runs[rid].role = role;
  runs[rid].step = 0;
  runs[rid].firstReal = 0;

  /* Now we need to create local terms corresponding to rho, sigma, and any local constants.
   *
   * We maintain our stuff in a from/to list.
   */

  // Create rho, sigma, constants
  createLocals (protocol->rolenames, true, true);
  createLocals (role->declaredvars, true, false);
  createLocals (role->declaredconsts, false, false);

  /* Now we prefix the read before rd, if extterm is not NULL.  Even if
   * extterm is NULL, rd is still set as the start and the index pointer of
   * the run.
   */
  run_prefix_read (sys, rid, rd, extterm);

  /* TODO this is not what we want yet, also local knowledge. The local
   * knowledge (list?) also needs to be substituted on invocation. */
  runs[rid].know = NULL;

  /* now adjust the local run copy */
  run_localize (sys, rid, fromlist, tolist, substlist);

  termlistDelete (fromlist);
  runs[rid].locals = tolist;

  /* erase any substitutions in the role definition, as they are now copied */
  termlistSubstReset (role->variables);

  /* length */
  runs[rid].rolelength = roledef_length (runs[rid].start);
  /* [[[ Hack ]]] this length is minimally 3 (to help the construction of the encryptors/decryptors from bare roledefs */
  if (runs[rid].rolelength < 3)
    {
      runs[rid].rolelength = 3;
    }

  /* new graph to create */
  dependPushRun (sys);
}


//! Instantiate a role by making a new run
/**
 * Just forwards to Arachne version.
 *
 * This involves creation of a new run(id).
 * Copy & subst of Roledef, Agent knowledge.
 * Tolist might contain type constants.
*/
void
roleInstance (const System sys, const Protocol protocol, const Role role,
	      const Termlist paramlist, Termlist substlist)
{
  roleInstanceArachne (sys, protocol, role, paramlist, substlist);
}

//! Destroy roleInstance
/**
 * Destroys the run with the highest index number
 */
void
roleInstanceDestroy (const System sys)
{
  if (sys->maxruns > 0)
    {
      int runid;
      struct run myrun;
      Termlist substlist;

      runid = sys->maxruns - 1;
      myrun = sys->runs[runid];

      // Reset graph
      dependPopRun ();

      // Destroy roledef
      roledefDestroy (myrun.start);

      // Destroy artefacts
      //
      termlistDelete (myrun.rho);
      termlistDelete (myrun.sigma);
      termlistDelete (myrun.constants);

      // sys->variables might contain locals from the run: remove them
      {
	Termlist tl;

	tl = sys->variables;
	while (tl != NULL)
	  {
	    Term t;

	    t = tl->term;
	    if (realTermLeaf (t) && TermRunid (t) == runid)
	      {
		Termlist tlnext;

		tlnext = tl->next;
		// remove from list; return pointer to head
		sys->variables = termlistDelTerm (tl);
		tl = tlnext;
	      }
	    else
	      {
		// proceed
		tl = tl->next;
	      }
	  }
      }

      /**
       * Undo the local copies of the substitutions. We cannot restore them however, so this might
       * prove a problem. We assume that the substlist fixes this at roleInstance time; it should be exact.
       */
      substlist = myrun.substitutions;
      while (substlist != NULL)
	{
	  Term t;

	  t = substlist->term;
	  if (t->subst != NULL)
	    {
	      termDelete (t->subst);
	      t->subst = NULL;
	    }
	  substlist = substlist->next;
	}
      termlistDelete (myrun.substitutions);

      /*
       * Artefact removal can only be done if knowledge sets are empty, as with Arachne
       */
      {
	Termlist artefacts;
	// Remove artefacts
	artefacts = myrun.artefacts;
	while (artefacts != NULL)
	  {
	    free (artefacts->term);
	    artefacts = artefacts->next;
	  }
      }

      // remove lists
      termlistDelete (myrun.artefacts);
      termlistDelete (myrun.locals);

      // Destroy run struct allocation in array using realloc
      // Reduce run count
      sys->maxruns = sys->maxruns - 1;
      sys->runs =
	(Run) realloc (sys->runs, sizeof (struct run) * (sys->maxruns));
    }
}

//! Initialise the second system phase.
/**
 * Allocates memory for traces.
 * The number of runs has to be known for this procedure.
 *\sa systemInit()
 */

void
systemStart (const System sys)
{
  int i, s;
  Roledef rd;

  s = 0;
  for (i = 0; i < sys->maxruns; i++)
    {
      rd = runPointerGet (sys, i);
      while (rd != NULL)
	{
	  s++;
	  rd = rd->next;
	}
    }

  /* this is the maximum trace length */
  if (sys->maxtracelength > s)
    sys->maxtracelength = s;

  /* trace gets one added entry for buffer */
  s = sys->maxtracelength + 1;

  /* freed in systemDone */
  sys->traceEvent = malloc (s * sizeof (Roledef));
  sys->traceRun = malloc (s * sizeof (int));
  sys->traceKnow = malloc (s * sizeof (Knowledge));
  sys->traceNode = malloc (s * sizeof (states_t));

  /* clear, for niceties */
  for (i = 0; i < s; i++)
    {
      sys->traceEvent[i] = NULL;
      sys->traceRun[i] = 0;
      sys->traceKnow[i] = NULL;
      sys->traceNode[i] = STATES0;
    }
}

//! Print the prefix of a line suitable for the current indent level.
void
indent ()
{
  int i = indentDepth;
  int j = 0;
  while (i > 0)
    {
      eprintf ("%i  ", j);
      i--;
      j++;
    }
}

//! Create an empty protocol structure with a name.
Protocol
protocolCreate (Term name)
{
  Protocol p;

  p = malloc (sizeof (struct protocol));
  p->nameterm = name;
  p->roles = NULL;
  p->rolenames = NULL;
  p->locals = NULL;
  p->next = NULL;
  p->lineno = 0;
  return p;
}

//! Print all local terms in a term list.
//@todo What is this doing here? This should be in termlists.c!
void
locVarPrint (Termlist tl)
{
  if (tl == NULL)
    {
      eprintf ("No local terms.\n");
    }
  else
    {
      eprintf ("Local terms: ");
      eprintf ("[");
      while (tl != NULL)
	{
	  termPrint (tl->term);
	  if (tl->term->stype != NULL)
	    {
	      eprintf (":");
	      termlistPrint (tl->term->stype);
	    }
	  tl = tl->next;
	  if (tl != NULL)
	    eprintf (",");
	}
      eprintf ("]");
      eprintf ("\n");
    }
}

//! Print a protocol.
void
protocolPrint (Protocol p)
{
  if (p == NULL)
    return;

  indent ();
  eprintf ("[[Protocol : ");
  termPrint (p->nameterm);
  eprintf (" (");
  termlistPrint (p->rolenames);
  eprintf (")]]\n");
  locVarPrint (p->locals);
  rolesPrint (p->roles);
}

//! Print a list of protocols.
void
protocolsPrint (Protocol p)
{
  while (p != NULL)
    {
      protocolPrint (p);
      p = p->next;
    }
}

//! Determine whether an agent term is trusted
/**
 * 1 (True) means trusted, 0 is untrusted
 */
int
isAgentTrusted (const System sys, Term agent)
{
  agent = deVar (agent);
  if (!realTermVariable (agent) && inTermlist (sys->untrusted, agent))
    {
      // Untrusted agent in the list
      return 0;
    }
  return 1;
}

//! Determine whether there is an untrusted agent.
/**
 *@return True iff all agent in the list are trusted.
 */
int
isAgentlistTrusted (const System sys, Termlist agents)
{
  while (agents != NULL)
    {
      if (!isAgentTrusted (sys, agents->term))
	{
	  return 0;
	}
      agents = agents->next;
    }
  return 1;
}

//! Determine whether all agents of a run are trusted
/**
 * Returns 0 (False) if they are not trusted, otherwise 1 (True)
 */
int
isRunTrusted (const System sys, const int run)
{
  if (run >= 0 && run < sys->maxruns)
    {
      if (!isAgentlistTrusted (sys, sys->runs[run].rho))
	{
	  return 0;
	}
    }
  return 1;
}

void
commandlinePrint (FILE * stream)
{
  /* print command line */
  int i;

  for (i = 0; i < switches.argc; i++)
    fprintf (stream, " %s", switches.argv[i]);
}

//! Get the number of roles in the system.
int
compute_rolecount (const System sys)
{
  Protocol pr;
  int n;

  n = 0;
  pr = sys->protocols;
  while (pr != NULL)
    {
      n = n + termlistLength (pr->rolenames);
      pr = pr->next;
    }
  return n;
}

//! Compute the maximum number of events in a single role in the system.
int
compute_roleeventmax (const System sys)
{
  Protocol pr;
  int maxev;

  maxev = 0;
  pr = sys->protocols;
  while (pr != NULL)
    {
      Role r;

      r = pr->roles;
      while (r != NULL)
	{
	  Roledef rd;
	  int n;

	  rd = r->roledef;
	  n = 0;
	  while (rd != NULL)
	    {
	      n++;
	      rd = rd->next;
	    }
	  if (n > maxev)
	    maxev = n;
	  r = r->next;
	}
      pr = pr->next;
    }
  return maxev;
}

//! Determine whether we don't need any more attacks
/**
 * Returns 1 (true) iff no more attacks are needed for this claim.
 */
int
enoughAttacks (const System sys)
{
  // Only if we are outputting more than one
  if (switches.prune == 0)
    {
      if (switches.maxAttacks != 0)
	{
	  // Note: we're comparing states (some type of large integer) to regular
	  // integers, and hope that the compiler solves it :)
	  if (sys->current_claim->failed >= switches.maxAttacks)
	    {
	      return 1;
	    }
	}
    }
  return 0;
}

//! Iterate over runs.
/**
 * Callback should return true in order to continue.
 */
int
iterateRuns (const System sys, int (*callback) (int r))
{
  int r;

  for (r = 0; r < sys->maxruns; r++)
    {
      if (!callback (r))
	{
	  return false;
	}
    }
  return true;
}

//! Iterate over non-intruder runs.
/**
 * Callback should return true in order to continue.
 */
int
iterateRegularRuns (const System sys, int (*callback) (int r))
{
  int regular (int r)
  {
    if (sys->runs[r].protocol != INTRUDER)
      {
	return callback (r);
      }
    return true;
  }

  return iterateRuns (sys, regular);
}

// Iterate over events in a certain run (increasing through role)
int
iterateEvents (const System sys, const int run,
	       int (*callback) (Roledef rd, int ev))
{
  int e;
  Roledef rd;

  rd = sys->runs[run].start;
  for (e = 0; e < sys->runs[run].step; e++)
    {
      if (!callback (rd, e))
	{
	  return false;
	}
      rd = rd->next;
    }
  return true;
}

//! Iterate over all events in all runs
/**
 * This includes intruder as well as regular events
 */
int
iterateAllEvents (const System sys,
		  int (*callback) (int run, Roledef rd, int ev))
{
  int run;

  int callwrapper (Roledef rd, int ev)
  {
    return callback (run, rd, ev);
  }

  for (run = 0; run < sys->maxruns; run++)
    {
      if (!iterateEvents (sys, run, callwrapper))
	{
	  return false;
	}
    }
  return true;
}

//! Iterate over event type in a certain run (increasing through role)
/**
 * If evtype == ANYEVENT then it does not matter.
 */
int
iterateEventsType (const System sys, const int run, const int evtype,
		   int (*callback) (Roledef rd, int ev))
{
  int selectEvent (Roledef rd, int e)
  {
    if (evtype == ANYEVENT || rd->type == evtype)
      {
	return callback (rd, e);
      }
    return true;
  }

  return iterateEvents (sys, run, selectEvent);
}

// Iterate over all 'others': local variables of a run that are instantiated and contain some term of another run.
int
iterateLocalToOther (const System sys, const int myrun,
		     int (*callback) (Term tlocal))
{
  Termlist tlo, tls;
  int flag;

  int addOther (Term t)
  {
    tlo = termlistAddNew (tlo, t);
    return true;
  }

  flag = true;
  tlo = NULL;
  // construct all others occuring in the reads
  for (tls = sys->runs[myrun].sigma; tls != NULL; tls = tls->next)
    {
      Term tt;

      tt = tls->term;
      if (realTermVariable (tt) && tt->subst != NULL);
      {
	iterateTermOther (myrun, tt->subst, addOther);
      }
    }
  // now iterate over all of them
  for (tls = tlo; flag && (tls != NULL); tls = tls->next)
    {
      if (!callback (tls->term))
	{
	  flag = false;
	}
    }

  // clean up
  termlistDelete (tlo);
  return flag;
}

//! Iterate over all roles
int
iterateRoles (const System sys, int (*callback) (Protocol p, Role r))
{
  Protocol p;

  p = sys->protocols;
  while (p != NULL)
    {
      Role r;

      r = p->roles;
      while (r != NULL)
	{
	  if (!callback (p, r))
	    {
	      return false;
	    }
	  r = r->next;
	}
      p = p->next;
    }
  return true;
}

//! Get first read/send occurrence (event index) of term t in run r
int
firstOccurrence (const System sys, const int r, Term t, int evtype)
{
  int firste;

  int checkOccurs (Roledef rd, int e)
  {
    if (termSubTerm (rd->message, t) || termSubTerm (rd->from, t)
	|| termSubTerm (rd->to, t))
      {
	firste = e;
	return false;
      }
    return true;
  }

  firste = -1;
  iterateEventsType (sys, r, evtype, checkOccurs);
#ifdef DEBUG
  if (DEBUGL (3))
    {
      if (firste == -1)
	{
	  globalError++;
	  eprintf ("Warning: Desired term ");
	  termPrint (t);
	  eprintf (" does not occur");
	  eprintf (" in run %i in event type %i.\n", r, evtype);
	  runPrint (sys->runs[r].start);
	  eprintf ("\n");
	  globalError--;
	}
    }
#endif
  return firste;
}

//! Get the roledef of an event
Roledef
eventRoledef (const System sys, const int run, const int ev)
{
  return roledef_shift (sys->runs[run].start, ev);
}

//! count the number of initiators
int
countInitiators (const System sys)
{
  int run;
  int count;

  count = 0;
  for (run = 0; run < sys->maxruns; run++)
    {
      if (sys->runs[run].role->initiator)
	{
	  count++;
	}
    }
  return count;
}

//! determine whether a run talks to itself
int
selfSession (const System sys, const int run)
{
  int self_session;
  Termlist agents;
  Termlist seen;

  if (sys->runs[run].protocol == INTRUDER)
    {
      // Intruder has no self sessions
      return false;
    }

  self_session = false;

  agents = sys->runs[run].rho;
  seen = NULL;
  while (agents != NULL)
    {
      Term agent;

      agent = deVar (agents->term);
      if (inTermlist (seen, agent))
	{
	  // This agent was already in the seen list
	  self_session = true;
	  break;
	}
      else
	{
	  seen = termlistAdd (seen, agent);
	}
      agents = agents->next;
    }
  termlistDelete (seen);

  return self_session;
}

//! determine whether a run is a so-called self-responder
/**
 * Alice starting a run with Bob, Charlie, Bob is also counted as self-response.
 */
int
selfResponder (const System sys, const int run)
{
  if (sys->runs[run].role->initiator)
    {
      return false;
    }
  else
    {
      return selfSession (sys, run);
    }
}

//! Count the number of any self-responders
int
selfResponders (const System sys)
{
  int count;
  int run;

  count = 0;
  run = 0;
  while (run < sys->maxruns)
    {
      if (selfResponder (sys, run))
	{
	  count++;
	}
      run++;
    }
  return count;
}

//! determine whether a run is a so-called self-initiator
/**
 * Alice starting a run with Bob, Charlie, Bob is also counted as self-initiation.
 */
int
selfInitiator (const System sys, const int run)
{
  if (sys->runs[run].role->initiator)
    {
      return selfSession (sys, run);
    }
  else
    {
      return false;
    }
}

//! Count the number of any self-initiators
int
selfInitiators (const System sys)
{
  int count;
  int run;

  count = 0;
  run = 0;
  while (run < sys->maxruns)
    {
      if (selfInitiator (sys, run))
	{
	  count++;
	}
      run++;
    }
  return count;
}
