//----------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All Rights Reserved. // //----------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.IO; using System.Linq; using System.Diagnostics; using System.Diagnostics.Contracts; namespace Microsoft.Boogie { public class CommandLineOptionEngine { public readonly string ToolName; public readonly string DescriptiveToolName; [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(ToolName != null); Contract.Invariant(DescriptiveToolName != null); Contract.Invariant(this._environment != null); Contract.Invariant(cce.NonNullElements(this._files)); Contract.Invariant(this._fileTimestamp != null); } private string/*!*/ _environment = ""; public string Environment { get { Contract.Ensures(Contract.Result() != null); return this._environment; } set { Contract.Requires(value != null); this._environment = value; } } private readonly List/*!*/ _files = new List(); public IList/*!*/ Files { get { Contract.Ensures(cce.NonNullElements(Contract.Result>())); Contract.Ensures(Contract.Result>().IsReadOnly); return this._files.AsReadOnly(); } } public bool HelpRequested = false; public bool AttrHelpRequested = false; public CommandLineOptionEngine(string toolName, string descriptiveName) { Contract.Requires(toolName != null); Contract.Requires(descriptiveName != null); ToolName = toolName; DescriptiveToolName = descriptiveName; } public virtual string/*!*/ VersionNumber { get { Contract.Ensures(Contract.Result() != null); return cce.NonNull(cce.NonNull(System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location)).FileVersion); } } public virtual string/*!*/ VersionSuffix { get { Contract.Ensures(Contract.Result() != null); return " version " + VersionNumber + ", Copyright (c) 2003-2014, Microsoft."; } } public virtual string/*!*/ Version { get { Contract.Ensures(Contract.Result() != null); return DescriptiveToolName + VersionSuffix; } } private string/*!*/ _fileTimestamp = cce.NonNull(DateTime.Now.ToString("o")).Replace(':', '.'); public string FileTimestamp { get { Contract.Ensures(Contract.Result() != null); return this._fileTimestamp; } set { Contract.Requires(value != null); this._fileTimestamp = value; } } public void ExpandFilename(ref string pattern, string logPrefix, string fileTimestamp) { if (pattern != null) { pattern = pattern.Replace("@PREFIX@", logPrefix).Replace("@TIME@", fileTimestamp); string fn = Files.Count == 0 ? "" : Files[Files.Count - 1]; fn = fn.Replace('/', '-').Replace('\\', '-'); pattern = pattern.Replace("@FILE@", fn); } } /// /// Process the option and modify "ps" accordingly. /// Return true if the option is one that is recognized. /// protected virtual bool ParseOption(string name, CommandLineParseState ps) { Contract.Requires(name != null); Contract.Requires(ps != null); switch (name) { case "help": case "?": if (ps.ConfirmArgumentCount(0)) { HelpRequested = true; } return true; case "attrHelp": if (ps.ConfirmArgumentCount(0)) { AttrHelpRequested = true; } return true; default: break; } return false; // unrecognized option } protected class CommandLineParseState { public string s; public bool hasColonArgument; public readonly string[]/*!*/ args; public int i; public int nextIndex; public bool EncounteredErrors; public readonly string ToolName; [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(args != null); Contract.Invariant(0 <= i && i <= args.Length); Contract.Invariant(0 <= nextIndex && nextIndex <= args.Length); } public CommandLineParseState(string[] args, string toolName) { Contract.Requires(args != null); Contract.Requires(Contract.ForAll(0, args.Length, i => args[i] != null)); Contract.Requires(toolName != null); Contract.Ensures(this.args == args); this.ToolName = toolName; this.s = null; // set later by client this.hasColonArgument = false; // set later by client this.args = args; this.i = 0; this.nextIndex = 0; // set later by client this.EncounteredErrors = false; } public bool CheckBooleanFlag(string flagName, ref bool flag, bool valueWhenPresent) { Contract.Requires(flagName != null); //modifies nextIndex, encounteredErrors, Console.Error.*; bool flagPresent = false; if ((s == "/" + flagName || s == "-" + flagName) && ConfirmArgumentCount(0)) { flag = valueWhenPresent; flagPresent = true; } return flagPresent; } public bool CheckBooleanFlag(string flagName, ref bool flag) { Contract.Requires(flagName != null); //modifies nextIndex, encounteredErrors, Console.Error.*; return CheckBooleanFlag(flagName, ref flag, true); } /// /// If there is one argument and it is a non-negative integer, then set "arg" to that number and return "true". /// Otherwise, emit error message, leave "arg" unchanged, and return "false". /// public bool GetNumericArgument(ref int arg) { //modifies nextIndex, encounteredErrors, Console.Error.*; return GetNumericArgument(ref arg, a => 0 <= a); } /// /// If there is one argument and the filtering predicate holds, then set "arg" to that number and return "true". /// Otherwise, emit error message, leave "arg" unchanged, and return "false". /// public bool GetNumericArgument(ref int arg, Predicate filter) { Contract.Requires(filter != null); if (this.ConfirmArgumentCount(1)) { try { Contract.Assume(args[i] != null); Contract.Assert(args[i] is string); // needed to prove args[i].IsPeerConsistent int d = Convert.ToInt32(this.args[this.i]); if (filter == null || filter(d)) { arg = d; return true; } } catch (System.FormatException) { } catch (System.OverflowException) { } } else { return false; } Error("Invalid argument \"{0}\" to option {1}", args[this.i], this.s); return false; } /// /// If there is one argument and it is a non-negative integer less than "limit", /// then set "arg" to that number and return "true". /// Otherwise, emit error message, leave "arg" unchanged, and return "false". /// public bool GetNumericArgument(ref int arg, int limit) { Contract.Requires(this.i < args.Length); Contract.Ensures(Math.Min(arg, 0) <= Contract.ValueAtReturn(out arg) && Contract.ValueAtReturn(out arg) < limit); //modifies nextIndex, encounteredErrors, Console.Error.*; int a = arg; if (!GetNumericArgument(ref a)) { return false; } else if (a < limit) { arg = a; return true; } else { Error("Invalid argument \"{0}\" to option {1}", args[this.i], this.s); return false; } } /// /// If there is one argument and it is a non-negative real, then set "arg" to that number and return "true". /// Otherwise, emit an error message, leave "arg" unchanged, and return "false". /// public bool GetNumericArgument(ref double arg) { Contract.Ensures(Contract.ValueAtReturn(out arg) >= 0); //modifies nextIndex, encounteredErrors, Console.Error.*; if (this.ConfirmArgumentCount(1)) { try { Contract.Assume(args[i] != null); Contract.Assert(args[i] is string); // needed to prove args[i].IsPeerConsistent double d = Convert.ToDouble(this.args[this.i]); if (0 <= d) { arg = d; return true; } } catch (System.FormatException) { } catch (System.OverflowException) { } } else { return false; } Error("Invalid argument \"{0}\" to option {1}", args[this.i], this.s); return false; } public bool ConfirmArgumentCount(int argCount) { Contract.Requires(0 <= argCount); //modifies nextIndex, encounteredErrors, Console.Error.*; Contract.Ensures(Contract.Result() == (!(hasColonArgument && argCount != 1) && !(args.Length < i + argCount))); if (hasColonArgument && argCount != 1) { Error("\"{0}\" cannot take a colon argument", s); nextIndex = args.Length; return false; } else if (args.Length < i + argCount) { Error("\"{0}\" expects {1} argument{2}", s, argCount.ToString(), (string)(argCount == 1 ? "" : "s")); nextIndex = args.Length; return false; } else { nextIndex = i + argCount; return true; } } public void Error(string message, params string[] args) { Contract.Requires(args != null); Contract.Requires(message != null); //modifies encounteredErrors, Console.Error.*; Console.Error.WriteLine("{0}: Error: {1}", ToolName, String.Format(message, args)); EncounteredErrors = true; } } public virtual void Usage() { Console.WriteLine("{0}: usage: {0} [ option ... ] [ filename ... ]", ToolName); Console.WriteLine(@" where