diff --git a/Cmdline/Action/AuthToken.cs b/Cmdline/Action/AuthToken.cs index 5bb806fc5b..008220950d 100644 --- a/Cmdline/Action/AuthToken.cs +++ b/Cmdline/Action/AuthToken.cs @@ -23,7 +23,7 @@ public AuthToken() { } /// /// Exit code /// - public int RunSubCommand(SubCommandOptions unparsed) + public int RunSubCommand(KSPManager manager, CommonOptions opts, SubCommandOptions unparsed) { string[] args = unparsed.options.ToArray(); int exitCode = Exit.OK; @@ -33,8 +33,12 @@ public int RunSubCommand(SubCommandOptions unparsed) if (!string.IsNullOrEmpty(option) && suboptions != null) { CommonOptions options = (CommonOptions)suboptions; + options.Merge(opts); user = new ConsoleUser(options.Headless); - manager = new KSPManager(user); + if (manager == null) + { + manager = new KSPManager(user); + } exitCode = options.Handle(manager, user); if (exitCode == Exit.OK) { @@ -115,7 +119,6 @@ private int removeAuthToken(RemoveAuthTokenOptions opts) private const string tokenHeader = "Token"; private IUser user; - private KSPManager manager; private static readonly ILog log = LogManager.GetLogger(typeof(AuthToken)); } diff --git a/Cmdline/Action/CompatSubCommand.cs b/Cmdline/Action/Compat.cs similarity index 94% rename from Cmdline/Action/CompatSubCommand.cs rename to Cmdline/Action/Compat.cs index 2e066193c9..86d54a545c 100644 --- a/Cmdline/Action/CompatSubCommand.cs +++ b/Cmdline/Action/Compat.cs @@ -5,9 +5,9 @@ namespace CKAN.CmdLine.Action { - public class CompatSubCommand : ISubCommand + public class Compat : ISubCommand { - public CompatSubCommand() { } + public Compat() { } public class CompatOptions : VerbCommandOptions { @@ -65,7 +65,7 @@ public class CompatForgetOptions : InstanceSpecificOptions [ValueOption(0)] public string Version { get; set; } } - public int RunSubCommand(SubCommandOptions options) + public int RunSubCommand(KSPManager manager, CommonOptions opts, SubCommandOptions options) { var exitCode = Exit.OK; @@ -75,9 +75,10 @@ public int RunSubCommand(SubCommandOptions options) if (!string.IsNullOrEmpty(option) && suboptions != null) { CommonOptions comOpts = (CommonOptions)suboptions; - _user = new ConsoleUser(comOpts.Headless); - _kspManager = new KSPManager(_user); - exitCode = comOpts.Handle(_kspManager, _user); + comOpts.Merge(opts); + _user = new ConsoleUser(comOpts.Headless); + _kspManager = manager ?? new KSPManager(_user); + exitCode = comOpts.Handle(_kspManager, _user); if (exitCode != Exit.OK) return; @@ -88,7 +89,7 @@ public int RunSubCommand(SubCommandOptions options) var ksp = MainClass.GetGameInstance(_kspManager); const string versionHeader = "Version"; - const string actualHeader = "Actual"; + const string actualHeader = "Actual"; var output = ksp .GetCompatibleVersions() @@ -196,7 +197,6 @@ public int RunSubCommand(SubCommandOptions options) } } }, () => { exitCode = MainClass.AfterHelp(); }); - RegistryManager.DisposeAll(); return exitCode; } diff --git a/Cmdline/Action/ISubCommand.cs b/Cmdline/Action/ISubCommand.cs index b87032b76d..27db3e41bc 100644 --- a/Cmdline/Action/ISubCommand.cs +++ b/Cmdline/Action/ISubCommand.cs @@ -2,7 +2,6 @@ { internal interface ISubCommand { - int RunSubCommand(SubCommandOptions options); + int RunSubCommand(KSPManager manager, CommonOptions opts, SubCommandOptions options); } } - diff --git a/Cmdline/Action/KSP.cs b/Cmdline/Action/KSP.cs index 58d8223d65..b158e39ee5 100644 --- a/Cmdline/Action/KSP.cs +++ b/Cmdline/Action/KSP.cs @@ -13,16 +13,16 @@ public KSP() { } internal class KSPSubOptions : VerbCommandOptions { [VerbOption("list", HelpText = "List KSP installs")] - public CommonOptions ListOptions { get; set; } + public CommonOptions ListOptions { get; set; } [VerbOption("add", HelpText = "Add a KSP install")] - public AddOptions AddOptions { get; set; } + public AddOptions AddOptions { get; set; } [VerbOption("rename", HelpText = "Rename a KSP install")] - public RenameOptions RenameOptions { get; set; } + public RenameOptions RenameOptions { get; set; } [VerbOption("forget", HelpText = "Forget a KSP install")] - public ForgetOptions ForgetOptions { get; set; } + public ForgetOptions ForgetOptions { get; set; } [VerbOption("default", HelpText = "Set the default KSP install")] public DefaultOptions DefaultOptions { get; set; } @@ -94,7 +94,7 @@ internal class DefaultOptions : CommonOptions } // This is required by ISubCommand - public int RunSubCommand(SubCommandOptions unparsed) + public int RunSubCommand(KSPManager manager, CommonOptions opts, SubCommandOptions unparsed) { string[] args = unparsed.options.ToArray(); @@ -123,8 +123,9 @@ public int RunSubCommand(SubCommandOptions unparsed) if (!string.IsNullOrEmpty(option) && suboptions != null) { CommonOptions options = (CommonOptions)suboptions; - User = new ConsoleUser(options.Headless); - Manager = new KSPManager(User); + options.Merge(opts); + User = new ConsoleUser(options.Headless); + Manager = manager ?? new KSPManager(User); exitCode = options.Handle(Manager, User); if (exitCode != Exit.OK) return; @@ -159,7 +160,6 @@ public int RunSubCommand(SubCommandOptions unparsed) } } }, () => { exitCode = MainClass.AfterHelp(); }); - RegistryManager.DisposeAll(); return exitCode; } diff --git a/Cmdline/Action/Prompt.cs b/Cmdline/Action/Prompt.cs new file mode 100644 index 0000000000..205d5fda9c --- /dev/null +++ b/Cmdline/Action/Prompt.cs @@ -0,0 +1,47 @@ +using System; +using CommandLine; +using CommandLine.Text; +using log4net; + +namespace CKAN.CmdLine +{ + + public class Prompt + { + public Prompt() { } + + public int RunCommand(KSPManager manager, object raw_options) + { + CommonOptions opts = raw_options as CommonOptions; + bool done = false; + while (!done) + { + // Prompt if not in headless mode + if (!(opts?.Headless ?? false)) + { + Console.Write( + manager.CurrentInstance != null + ? $"CKAN {Meta.GetVersion()}: KSP {manager.CurrentInstance.Version().ToString()} ({manager.CurrentInstance.Name})> " + : $"CKAN {Meta.GetVersion()}> " + ); + } + // Get input + string command = Console.ReadLine(); + if (command == null || command == exitCommand) + { + done = true; + } + else if (command != "") + { + // Parse input as if it was a normal command line, + // but with a persistent KSPManager object. + MainClass.Execute(manager, opts, command.Split(' ')); + } + } + return Exit.OK; + } + + private const string exitCommand = "exit"; + } + +} diff --git a/Cmdline/Action/Repair.cs b/Cmdline/Action/Repair.cs index cc3d3eb1d1..131cd7ccd1 100644 --- a/Cmdline/Action/Repair.cs +++ b/Cmdline/Action/Repair.cs @@ -39,7 +39,7 @@ public string GetUsage(string verb) } } - public int RunSubCommand(SubCommandOptions unparsed) + public int RunSubCommand(KSPManager manager, CommonOptions opts, SubCommandOptions unparsed) { int exitCode = Exit.OK; // Parse and process our sub-verbs @@ -49,8 +49,12 @@ public int RunSubCommand(SubCommandOptions unparsed) if (!string.IsNullOrEmpty(option) && suboptions != null) { CommonOptions options = (CommonOptions)suboptions; + options.Merge(opts); User = new ConsoleUser(options.Headless); - KSPManager manager = new KSPManager(User); + if (manager == null) + { + manager = new KSPManager(User); + } exitCode = options.Handle(manager, User); if (exitCode != Exit.OK) return; @@ -68,7 +72,6 @@ public int RunSubCommand(SubCommandOptions unparsed) } } }, () => { exitCode = MainClass.AfterHelp(); }); - RegistryManager.DisposeAll(); return exitCode; } @@ -79,7 +82,7 @@ public int RunSubCommand(SubCommandOptions unparsed) /// private int Registry(CKAN.KSP ksp) { - var manager = RegistryManager.Instance(ksp); + RegistryManager manager = RegistryManager.Instance(ksp); manager.registry.Repair(); manager.Save(); User.RaiseMessage("Registry repairs attempted. Hope it helped."); diff --git a/Cmdline/Action/Repo.cs b/Cmdline/Action/Repo.cs index 9e0dfb78c3..15a049370a 100644 --- a/Cmdline/Action/Repo.cs +++ b/Cmdline/Action/Repo.cs @@ -91,7 +91,7 @@ internal class ForgetOptions : InstanceSpecificOptions } // This is required by ISubCommand - public int RunSubCommand(SubCommandOptions unparsed) + public int RunSubCommand(KSPManager manager, CommonOptions opts, SubCommandOptions unparsed) { string[] args = unparsed.options.ToArray(); @@ -118,8 +118,9 @@ public int RunSubCommand(SubCommandOptions unparsed) if (!string.IsNullOrEmpty(option) && suboptions != null) { CommonOptions options = (CommonOptions)suboptions; - User = new ConsoleUser(options.Headless); - Manager = new KSPManager(User); + options.Merge(opts); + User = new ConsoleUser(options.Headless); + Manager = manager ?? new KSPManager(User); exitCode = options.Handle(Manager, User); if (exitCode != Exit.OK) return; @@ -154,7 +155,6 @@ public int RunSubCommand(SubCommandOptions unparsed) } } }, () => { exitCode = MainClass.AfterHelp(); }); - RegistryManager.DisposeAll(); return exitCode; } diff --git a/Cmdline/CKAN-cmdline.csproj b/Cmdline/CKAN-cmdline.csproj index b2750f681b..1392f4420f 100644 --- a/Cmdline/CKAN-cmdline.csproj +++ b/Cmdline/CKAN-cmdline.csproj @@ -56,7 +56,7 @@ - + @@ -64,6 +64,7 @@ + diff --git a/Cmdline/Main.cs b/Cmdline/Main.cs index 906e736bab..42ebf8d43f 100644 --- a/Cmdline/Main.cs +++ b/Cmdline/Main.cs @@ -56,6 +56,18 @@ public static int Main(string[] args) return Gui(new GuiOptions(), args); } + try + { + return Execute(null, null, args); + } + finally + { + RegistryManager.DisposeAll(); + } + } + + public static int Execute(KSPManager manager, CommonOptions opts, string[] args) + { // We shouldn't instantiate Options if it's a subcommand. // It breaks command-specific help, for starters. try @@ -63,19 +75,19 @@ public static int Main(string[] args) switch (args[0]) { case "repair": - return (new Repair()).RunSubCommand(new SubCommandOptions(args)); + return (new Repair()).RunSubCommand(manager, opts, new SubCommandOptions(args)); case "ksp": - return (new KSP()).RunSubCommand(new SubCommandOptions(args)); + return (new KSP()).RunSubCommand(manager, opts, new SubCommandOptions(args)); case "compat": - return (new CompatSubCommand()).RunSubCommand(new SubCommandOptions(args)); + return (new Compat()).RunSubCommand(manager, opts, new SubCommandOptions(args)); case "repo": - return (new Repo()).RunSubCommand(new SubCommandOptions(args)); + return (new Repo()).RunSubCommand(manager, opts, new SubCommandOptions(args)); case "authtoken": - return (new AuthToken()).RunSubCommand(new SubCommandOptions(args)); + return (new AuthToken()).RunSubCommand(manager, opts, new SubCommandOptions(args)); } } catch (NoGameInstanceKraken) @@ -103,8 +115,16 @@ public static int Main(string[] args) // Process commandline options. CommonOptions options = (CommonOptions)cmdline.options; + options.Merge(opts); IUser user = new ConsoleUser(options.Headless); - KSPManager manager = new KSPManager(user); + if (manager == null) + { + manager = new KSPManager(user); + } + else + { + manager.User = user; + } try { @@ -154,28 +174,31 @@ private static int RunSimpleAction(Options cmdline, CommonOptions options, strin case "consoleui": return ConsoleUi(options, args); + case "prompt": + return new Prompt().RunCommand(manager, cmdline.options); + case "version": return Version(user); case "update": - return (new Update(user)).RunCommand(GetGameInstance(manager), (UpdateOptions)cmdline.options); + return (new Update(user)).RunCommand(GetGameInstance(manager), cmdline.options); case "available": - return (new Available(user)).RunCommand(GetGameInstance(manager), (AvailableOptions)cmdline.options); + return (new Available(user)).RunCommand(GetGameInstance(manager), cmdline.options); case "add": case "install": Scan(GetGameInstance(manager), user, cmdline.action); - return (new Install(user)).RunCommand(GetGameInstance(manager), (InstallOptions)cmdline.options); + return (new Install(user)).RunCommand(GetGameInstance(manager), cmdline.options); case "scan": return Scan(GetGameInstance(manager), user); case "list": - return (new List(user)).RunCommand(GetGameInstance(manager), (ListOptions)cmdline.options); + return (new List(user)).RunCommand(GetGameInstance(manager), cmdline.options); case "show": - return (new Show(user)).RunCommand(GetGameInstance(manager), (ShowOptions)cmdline.options); + return (new Show(user)).RunCommand(GetGameInstance(manager), cmdline.options); case "search": return (new Search(user)).RunCommand(GetGameInstance(manager), options); @@ -206,10 +229,6 @@ private static int RunSimpleAction(Options cmdline, CommonOptions options, strin { return printMissingInstanceError(user); } - finally - { - RegistryManager.DisposeAll(); - } } private static int printMissingInstanceError(IUser user) diff --git a/Cmdline/Options.cs b/Cmdline/Options.cs index 14ad13f1d7..fe88650b03 100644 --- a/Cmdline/Options.cs +++ b/Cmdline/Options.cs @@ -15,7 +15,7 @@ namespace CKAN.CmdLine public class Options { - public string action { get; set; } + public string action { get; set; } public object options { get; set; } /// @@ -28,7 +28,7 @@ public Options(string[] args) ( args, new Actions(), (verb, suboptions) => { - action = verb; + action = verb; options = suboptions; }, delegate @@ -49,6 +49,9 @@ internal class Actions : VerbCommandOptions [VerbOption("consoleui", HelpText = "Start the CKAN console UI")] public ConsoleUIOptions ConsoleUIOptions { get; set; } + [VerbOption("prompt", HelpText = "Run CKAN prompt for executing multiple commands in a row")] + public CommonOptions PromptOptions { get; set; } + [VerbOption("search", HelpText = "Search for mods")] public SearchOptions SearchOptions { get; set; } @@ -100,7 +103,7 @@ internal class Actions : VerbCommandOptions [VerbOption("compare", HelpText = "Compare version strings")] public CompareOptions Compare { get; set; } - [VerbOption("version", HelpText = "Show the version of the CKAN client being used.")] + [VerbOption("version", HelpText = "Show the version of the CKAN client being used")] public VersionOptions Version { get; set; } [HelpVerbOption] @@ -244,6 +247,24 @@ public virtual int Handle(KSPManager manager, IUser user) return Exit.OK; } + /// + /// Combine two options objects. + /// This is mainly to ensure that --headless carries through for prompt. + /// + /// Options object to merge into this one + public void Merge(CommonOptions otherOpts) + { + if (otherOpts != null) + { + Verbose = Verbose || otherOpts.Verbose; + Debug = Debug || otherOpts.Debug; + Debugger = Debugger || otherOpts.Debugger; + NetUserAgent = NetUserAgent ?? otherOpts.NetUserAgent; + Headless = Headless || otherOpts.Headless; + AsRoot = AsRoot || otherOpts.AsRoot; + } + } + private static void CheckMonoVersion(IUser user, int rec_major, int rec_minor, int rec_patch) { try diff --git a/Core/ModuleInstaller.cs b/Core/ModuleInstaller.cs index 4f3ed0d43c..a0ab2d8bc3 100644 --- a/Core/ModuleInstaller.cs +++ b/Core/ModuleInstaller.cs @@ -213,7 +213,7 @@ public void InstallList(ICollection modules, RelationshipResolverOpt registry_manager.Save(!options.without_enforce_consistency); - User.RaiseProgress("Commiting filesystem changes", 80); + User.RaiseProgress("Committing filesystem changes", 80); transaction.Complete(); @@ -250,7 +250,7 @@ public void InstallList(ModuleResolution modules, RelationshipResolverOptions op registry_manager.Save(!options.without_enforce_consistency); - User.RaiseProgress("Commiting filesystem changes", 80); + User.RaiseProgress("Committing filesystem changes", 80); transaction.Complete(); }