/* * Scyther : An automatic verifier for security protocols. * Copyright (C) 2007-2013 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 switches.c * \brief Handle command-line options * * Contains the main switch handling. */ #include #include #include #include "system.h" #include "debug.h" #include "timer.h" #include "switches.h" #include "error.h" #include "specialterm.h" // Program name const char *progname = "scyther"; #include "version.h" // Structures struct switchdata switches; extern struct tacnode *spdltac; // Global char *lastfoundprefix = NULL; // Forward declarations void process_environment (void); int process_switches (int commandline); //! Init switches /** * Set them all to the default settings. */ void switchesInit (int argc, char **argv) { // Methods switches.match = 0; // default matching switches.tupling = 0; // Pruning and Bounding switches.prune = 2; // default pruning method (use nice heuristic) switches.maxproofdepth = INT_MAX; switches.maxtracelength = INT_MAX; switches.runs = 5; // default is 5 for usability, but -r 0 or --maxruns=0 will set it back to INT_MAX switches.filterProtocol = NULL; // default check all claims switches.filterLabel = NULL; // default check all claims switches.maxAttacks = 0; // no maximum default switches.maxOfRole = 0; // no maximum default switches.oneRolePerAgent = 0; // agents can perform multiple roles // Arachne switches.heuristic = 674; // default goal selection method (used to be 162) switches.maxIntruderActions = INT_MAX; // max number of encrypt/decrypt events switches.agentTypecheck = 1; // default do check agent types switches.concrete = true; // default removes symbols, and makes traces concrete switches.initUnique = false; // default allows initiator rho to contain duplicate terms switches.respUnique = false; // default allows responder rho to contain duplicate terms switches.roleUnique = false; // default allows agents to perform multiple roles switches.intruder = true; // default allows an intruder switches.chosenName = false; // default no chosen name attacks switches.agentUnfold = 0; // default not to unfold agents switches.abstractionMethod = 0; // default no abstraction used switches.useAttackBuffer = false; // don't use by default as it does not work properly under windows vista yet // Misc switches.switchP = 0; // multi-purpose parameter switches.experimental = 0; // experimental stuff defaults to 0, whatever that means. switches.removeclaims = false; // default: leave claims from spdl file switches.addreachableclaim = false; // add 'reachable' claims switches.addallclaims = false; // add all sorts of claims switches.check = false; // check the protocol for termination etc. (default off) switches.expert = false; // expert mode (off by default) // Output switches.output = SUMMARY; // default is to show a summary switches.report = 0; switches.reportClaims = 0; // default don't report on claims switches.xml = false; // default no xml output switches.dot = false; // default no dot output switches.human = false; // not human friendly by default switches.reportMemory = 0; switches.reportTime = 0; switches.countStates = false; // default off switches.extendNonRecvs = 0; // default off switches.extendTrivial = 0; // default off switches.plain = false; // default colors for terminal switches.monochrome = false; // default colors for dot switches.lightness = 0; // lightness correction switches.clusters = false; // default is no clusters for now switches.exitCodes = true; // default is to flag exit codes // Process the environment variable SCYTHERFLAGS process_environment (); // Process the command-line switches switches.argc = argc; switches.argv = argv; process_switches (true); } //! Exit void switchesDone (void) { if (lastfoundprefix != NULL) free (lastfoundprefix); } //! Open a (protocol) file. /** * Uses the environment variable SCYTHERDIR to also search for files * * If a file was opened before, this is stored in the static char* (initially * NULL) lastfoundprefix. This prefix can override any other: the point of it is that * if you find a file in a non-standard location, which then does an include, * you want this to refer to the directory where you found that last file. * * If reopener == NULL then we open a new file pointer, otherwise reopen this one. */ FILE * openFileSearch (char *filename, FILE * reopener) { const char *separators = ":;\n"; char *dirs; //! try a filename and a prefix. /** * Prefixes don't have to end with "/"; this will be added automatically. */ int try (char *prefix) { char *buffer = NULL; int result = false; int buflen = 0; int prefixlen = 0; int namelen = 0; int addslash = false; int nameindex = 0; prefixlen = (int) strcspn (prefix, separators); namelen = strlen (filename); nameindex = prefixlen; buflen = prefixlen + namelen + 1; // Does the prefix end with a slash? (it should) if (prefixlen > 0 && prefix[prefixlen - 1] != '/') { addslash = true; buflen++; nameindex++; } buffer = (char *) malloc (buflen); memcpy (buffer, prefix, prefixlen); memcpy (buffer + nameindex, filename, namelen); buffer[buflen - 1] = '\0'; // Add the slash in the center if (addslash) buffer[nameindex - 1] = '/'; // Now try to open it if (reopener != NULL) { if (freopen (buffer, "r", reopener) != NULL) result = true; } else { reopener = fopen (buffer, "r"); if (reopener != NULL) result = true; } if (result) { // There is a result. Does it have a prefix? char *ls; // Non-standard location, maybe we should warn for that if (switches.expert) { globalError++; eprintf ("Recving file %s.\n", buffer); globalError--; } // Compute the prefix (simply scan for the last slash, if any) ls = strrchr (buffer, '/'); if (ls != NULL) { // Store it for any next includes or something like that // Clear the old one if (lastfoundprefix != NULL) free (lastfoundprefix); ls[0] = '\0'; lastfoundprefix = buffer; return true; } } free (buffer); return result; } // main code. // Try last file prefix (if it exists!) if (lastfoundprefix != NULL) { if (try (lastfoundprefix)) return reopener; } // Try current directory if (try ("")) return reopener; // Now try the environment variable dirs = getenv ("SCYTHERDIR"); while (dirs != NULL) { if (strlen (dirs) > 0) { // try this one if (try (dirs)) return reopener; // skip to next dirs = strpbrk (dirs, separators); if (dirs != NULL) { // skip over separator dirs++; } } else { break; } } // Nope return NULL; } //! Open a (protocol) file instead of stdin int openFileStdin (char *filename) { if (openFileSearch (filename, stdin) == NULL) { return false; } else { return true; } } //! 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, int index, int commandline) { char *this_arg; // just a shortcut int this_arg_length; // same here int argc; char **argv; char *arg_pointer; //! 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]; } //! Retrieve a (string) argument char *string_argument (void) { char *result; if (arg_pointer == NULL) { error ("Argument expected."); } result = arg_pointer; arg_next (); return result; } //! 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]; } } // Alright, 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 = switches.argc; argv = switches.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 ('d', "dot-output", 0)) { if (!process) { helptext ("-d, --dot-output", "show patterns in dot format"); } else { switches.output = ATTACK; switches.dot = true; return index; } } if (detect ('x', "xml-output", 0)) { if (!process) { helptext ("-x, --xml-output", "show patterns in XML format"); } else { switches.output = ATTACK; switches.xml = true; return index; } } if (detect (' ', "proof", 0)) { if (!process) { /* * discourage: not very readable for non-experts yet helptext (" --proof", "show explicit proof"); */ } else { // Proof switches.output = PROOF; return index; } } if (detect (' ', "filter", 1)) { if (!process) { helptext ("--filter=[,