/** *@file switches.c * \brief Handle command-line options * * Contains the main switch handling. */ #include "string.h" #include "system.h" #include "debug.h" #include "version.h" #include "timer.h" extern System sys; extern int mgu_match; extern struct tacnode *spdltac; extern Term TERM_Claim; const char *progname = "scyther"; const char *releasetag = SVNVERSION; //! Process a single switch or generate help text /** * When process is false, we just generate the help text. * * Yields new index, or -1 when an error occurred. * When the new index > argc, it should not be called anymore. * By convention, argc = the number of arguments + 1 * The index steps through 1..argc-1. */ int switcher (const int process, const System sys, int index) { char *this_arg; // just a shortcut int this_arg_length; // same here int argc; char **argv; char *arg_pointer; int arg_index; //! Check whether there are still n options left int enough_arguments_left (const int n, char shortopt, char *longopt) { if (index + n > argc) { error ("Option %c [%s] needs at least %i arguments.", shortopt, longopt, n); } return 1; } // Skip over (processed) argument void arg_next (void) { index++; arg_pointer = argv[index]; } //! Parse an argument into an integer int integer_argument (void) { int result; if (arg_pointer == NULL) { error ("(Integer) argument expected."); } result = 0; if (sscanf (arg_pointer, "%i", &result) != 1) { error ("Could not parse expected integer argument."); } arg_next (); return result; } //! Detect whether this confirms to this option. /** * set arg_pointer and index */ int detect (char shortopt, char *longopt, int args) { arg_pointer = NULL; if (!process) { // If we are not processing, we always yield true. return 1; } // Is it this option anyway? if (this_arg_length < 2 || this_arg[0] != '-') { // No option return 0; } // Compare if (this_arg[1] == '-') { int optlength; // This seems to be a long switch, so we handle it accordingly optlength = strlen (longopt); if (strncmp (this_arg + 2, longopt, optlength)) return 0; if (optlength + 2 < this_arg_length) { // This has an additional thing! if (args > 0 && this_arg[2 + optlength] == '=') { // It's the right thing if (optlength + 3 < this_arg_length) { arg_pointer = this_arg + 2 + optlength + 1; } else { // arg = next index++; arg_pointer = argv[index]; } } else { // It's not this option return 0; } } else { // arg = next index++; arg_pointer = argv[index]; } } else { // Short variant if (this_arg_length < 2 || this_arg[1] != shortopt) return 0; if (args > 0 && this_arg_length > 2) { // This has an additional thing! // We assume the argument follows immediately (no appended '=') arg_pointer = this_arg + 2; } else { // arg = next index++; arg_pointer = argv[index]; } } // Allright, this is the right option // Enough arguments left? return enough_arguments_left (args, shortopt, longopt); } //! Align columns void helptext (const char *left, const char *right) { printf (" %-25s %s\n", left, right); } if (process) { argc = sys->argc; argv = sys->argv; #ifdef DEBUG // Check range for debug; we trust the non-debug version :) if (index < 1 || index >= argc) { error ("Bad index number %i for argc %i", index, argc); } #endif this_arg = argv[index]; this_arg_length = strlen (this_arg); } else { // Just doing help this_arg = NULL; this_arg_length = 0; } /* * ------------------------------------------------------------- * Process the options, one by one * ------------------------------------------------------------- */ /* ================== * Generic options */ if (detect ('a', "arachne", 0)) { if (!process) { helptext ("-a,--arachne", "select Arachne engine [modelchecker]"); } else { // Select arachne engine sys->engine = ARACHNE_ENGINE; bindingInit (sys); return index; } } if (detect ('x', "xml-output", 0)) { if (!process) { helptext ("-x,--xml-output", "show attack output in XML format"); } else { sys->switchXMLoutput = 1; return index; } } if (detect ('m', "match", 1)) { if (!process) { helptext ("-m,--match=", "matching method [0]"); } else { sys->match = integer_argument (); mgu_match = sys->match; return index; } } if (detect ('T', "timer", 1)) { if (!process) { /* Not shown in from help, as we don't want to encourage this helptext ("-T,--timer=", "maximum time in seconds [inf]"); */ } else { set_time_limit (integer_argument ()); return index; } } if (detect ('r', "max-runs", 1)) { if (!process) { helptext ("-r,--max-runs=", "maximum number of runs in the system [inf]"); } else { sys->switchRuns = integer_argument (); return index; } } if (detect ('l', "max-length", 1)) { if (!process) { helptext ("-l,--max-length=", "prune traces longer than events [inf]"); } else { sys->switch_maxtracelength = integer_argument (); return index; } } if (detect ('p', "prune", 1)) { if (!process) { /* not very important helptext ("-p,--prune=", "pruning method when an attack is found [0]"); */ } else { sys->prune = integer_argument (); return index; } } /* ================== * Modelchecker only */ if (!process) { printf ("Switches for modelchecking engine:\n"); } if (detect ('L', "latex", 0)) { if (!process) { helptext ("-L,--latex", "output attacks in LaTeX format [ASCII]"); } else { sys->latex = 1; return index; } } if (detect (' ', "state-space", 0)) { if (!process) { helptext ("--state-space", "output state space graph (in DOT format)"); } else { sys->output = STATESPACE; return index; } } /* ================== * Arachne only */ if (!process) { printf ("Switches for Arachne engine:\n"); helptext ("(fixed)", "output attacks in DOT format"); } if (detect ('G', "goal-select", 1)) { if (!process) { /* discourage: hide helptext ("-G,--goal-select=", "use goal selection method [3]"); */ } else { sys->switchGoalSelectMethod = integer_argument (); return index; } } if (detect ('P', "proof", 0)) { if (!process) { helptext ("-P,--proof", "show explicit proof"); } else { // Proof sys->output = PROOF; return index; } } /* ================== * External options */ if (!process) printf ("Misc. switches:\n"); if (detect ('E', "echo", 0)) { if (!process) { /* not very important helptext ("-E,--echo", "echo command line"); */ } else { /* print command line */ fprintf (stdout, "command\t"); commandlinePrint (stdout, sys); fprintf (stdout, "\n"); return index; } } if (detect (' ', "summary", 0)) { if (!process) { helptext ("--summary", "show summary only: omit attack details"); } else { sys->output = SUMMARY; return index; } } if (detect ('b', "progress-bar", 0)) { if (!process) { /* discourage: do not show in help text helptext ("-b,--progress-bar", "show progress bar"); */ } else { sys->switchS = 50000; return index; } } if (detect ('e', "empty", 0)) { if (!process) { /* not very important helptext ("-e,--empty", "do not generate output"); */ } else { sys->output = EMPTY; return index; } } if (detect ('v', "version", 0)) { if (!process) { /* not very important: hide helptext ("-v,--version", "version information"); */ } else { printf ("'%s' model checker for security protocols.\n", progname); #ifdef DEBUG printf ("Revision %s, compiled with debugging support.\n", SVNVERSION); #else printf ("Revision %s\n", SVNVERSION); #endif printf ("Code by Cas Cremers\n"); exit (0); } } if (detect ('h', "help", 0)) { if (!process) { helptext ("-h,--help", "show this help"); } else { printf ("Usage:\n"); printf (" %s [switches] [FILE]\nSwitches:\n", progname); switcher (0, NULL, 0); exit (0); } } #ifdef DEBUG if (detect ('D', "debug", 1)) { if (!process) { helptext ("-D,--debug=", "set debug (verbosity) level. [0]"); } else { debugSet (integer_argument ()); return index; } } #endif if (detect ('o', "output", 1)) { if (!process) { helptext ("-o,--output=", "output file [stdout]"); } else { // Set output file name /* try to open */ if (!freopen (arg_pointer, "w", stdout)) { fprintf (stderr, "Could not create output file '%s'.\n", arg_pointer); exit (1); } arg_next (); return index; } } // If the option is not recognized, it means a file name. if (!process) { helptext ("FILE", "input file ('-' for stdin)"); } else { if (!strcmp (this_arg, "-")) { // '-' input: Leave input to stdin } else { // not '-' input: change stdin to come from this file if (!freopen (this_arg, "r", stdin)) { // The file was not found. We have two options... if (this_arg[0] == '-') { fprintf (stderr, "Unknown switch '%s'.\n", this_arg); } else { fprintf (stderr, "Could not open input file '%s'.\n", this_arg); } exit (1); } return index + 1; } } return 0; } //! Process switches void process_switches (const System sys) { int index; if (sys->argc == 1) { printf ("Try '%s --help' for more information, or visit:\n", progname); printf (" http://www.win.tue.nl/~ccremers/scyther/index.html\n"); exit (0); } index = 1; while (index < sys->argc && index > 0) { index = switcher (1, sys, index); } }