From 2e502ea7be252614a895912ba773b82da34dd349 Mon Sep 17 00:00:00 2001 From: "Marcelo C." Date: Fri, 17 Jan 2025 14:00:14 -0300 Subject: [PATCH 1/2] Help Command unification All CLI tools and executables now accept a unified set of help command variants: "help", "/help","--help", "usage", "/usage", "--usage", "/h", "-h" Affected executables: Duplicati.Agent Duplicati.CommandLine Duplicati.CommandLine.AutoUpdater Duplicati.CommandLine.BackendTester Duplicati.CommandLine.BackendTool Duplicati.CommandLine.ConfigurationImporter Duplicati.CommandLine.SecretTool Duplicati.CommandLine.Snapshots Duplicati.GUI.TrayIcon Duplicati.Server Duplicati.Service Duplicati.WindowsService Note that SharpAesCrypt command is not included in this because the parameter parsing is part of the SharpAesCrypt code base. --- Duplicati/Agent/Program.cs | 39 ++++++------- .../CommandLine/BackendTester/Program.cs | 43 ++++++++------- Duplicati/CommandLine/BackendTool/Program.cs | 41 +++++++------- Duplicati/CommandLine/CLI/Program.cs | 43 ++++++++------- Duplicati/CommandLine/SecretTool/Program.cs | 6 +- Duplicati/CommandLine/ServerUtil/Program.cs | 2 + .../GUI/Duplicati.GUI.TrayIcon/Program.cs | 55 ++++++++----------- Duplicati/Library/Snapshots/Program.cs | 43 ++++++++------- .../Utility/Duplicati.Library.Utility.csproj | 1 + .../Library/Utility/HelpOptionExtensions.cs | 50 +++++++++++++++++ Duplicati/Server/Program.cs | 44 +++++++-------- Duplicati/WindowsService/Program.cs | 42 +++++++------- 12 files changed, 233 insertions(+), 176 deletions(-) create mode 100644 Duplicati/Library/Utility/HelpOptionExtensions.cs diff --git a/Duplicati/Agent/Program.cs b/Duplicati/Agent/Program.cs index 285784511..b5a9f7baa 100644 --- a/Duplicati/Agent/Program.cs +++ b/Duplicati/Agent/Program.cs @@ -1,22 +1,22 @@ -// Copyright (C) 2025, The Duplicati Team -// https://duplicati.com, hello@duplicati.com -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// Copyright (C) 2025, The Duplicati Team +// https://duplicati.com, hello@duplicati.com +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System.CommandLine; @@ -166,6 +166,7 @@ public static class Program context.ExitCode = 1; } }) + .UseAdditionalHelpAliases() .Build() .InvokeAsync(args); } diff --git a/Duplicati/CommandLine/BackendTester/Program.cs b/Duplicati/CommandLine/BackendTester/Program.cs index 9b8518521..0af39f96e 100644 --- a/Duplicati/CommandLine/BackendTester/Program.cs +++ b/Duplicati/CommandLine/BackendTester/Program.cs @@ -1,22 +1,22 @@ -// Copyright (C) 2025, The Duplicati Team -// https://duplicati.com, hello@duplicati.com -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// Copyright (C) 2025, The Duplicati Team +// https://duplicati.com, hello@duplicati.com +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; @@ -32,7 +32,7 @@ using Duplicati.StreamUtil; namespace Duplicati.CommandLine.BackendTester { public class Program - { + { class TempFile { public readonly string remotefilename; @@ -82,7 +82,8 @@ namespace Duplicati.CommandLine.BackendTester List args = new List(_args); Dictionary options = Library.Utility.CommandLineParser.ExtractOptions(args); - if (args.Count != 1 || String.Equals(args[0], "help", StringComparison.OrdinalIgnoreCase) || args[0] == "?") + if (args.Count != 1 || HelpOptionExtensions.AlternativeHelpStrings.Any(helpString => + args.Contains(helpString, StringComparer.OrdinalIgnoreCase))) { Console.WriteLine("Usage: ://:@"); Console.WriteLine("Example: ftp://user:pass@server/folder"); diff --git a/Duplicati/CommandLine/BackendTool/Program.cs b/Duplicati/CommandLine/BackendTool/Program.cs index 0e37d5541..eaed7fbf1 100644 --- a/Duplicati/CommandLine/BackendTool/Program.cs +++ b/Duplicati/CommandLine/BackendTool/Program.cs @@ -1,22 +1,22 @@ -// Copyright (C) 2025, The Duplicati Team -// https://duplicati.com, hello@duplicati.com -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// Copyright (C) 2025, The Duplicati Team +// https://duplicati.com, hello@duplicati.com +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using Duplicati.Library.Interface; @@ -74,7 +74,8 @@ namespace Duplicati.CommandLine.BackendTool } - if (args.Count < 2 || String.Equals(args[0], "help", StringComparison.OrdinalIgnoreCase) || args[0] == "?" || command == null) + if (args.Count < 2 || HelpOptionExtensions.AlternativeHelpStrings.Any(helpString => + args.Contains(helpString, StringComparer.OrdinalIgnoreCase)) || command == null) { if (command == null && args.Count >= 2) { diff --git a/Duplicati/CommandLine/CLI/Program.cs b/Duplicati/CommandLine/CLI/Program.cs index 66e7bb7a0..0fc00eaff 100644 --- a/Duplicati/CommandLine/CLI/Program.cs +++ b/Duplicati/CommandLine/CLI/Program.cs @@ -1,22 +1,22 @@ -// Copyright (C) 2025, The Duplicati Team -// https://duplicati.com, hello@duplicati.com -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// Copyright (C) 2025, The Duplicati Team +// https://duplicati.com, hello@duplicati.com +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; @@ -27,11 +27,13 @@ using System.IO; using Duplicati.Library.Interface; using Duplicati.Library.AutoUpdater; using Duplicati.Library.Crashlog; +using Duplicati.Library.Utility; namespace Duplicati.CommandLine { public class Program { + private static readonly string LOGTAG = Library.Logging.Log.LogTagFromType(); public static bool FROM_COMMANDLINE = false; @@ -163,7 +165,8 @@ namespace Duplicati.CommandLine } // Probe for "help" to avoid extra processing - if (cargs.Count == 0 || (string.Equals(cargs[0], "help", StringComparison.OrdinalIgnoreCase))) + if (cargs.Count == 0 || HelpOptionExtensions.AlternativeHelpStrings.Any(helpString => + cargs.Contains(helpString, StringComparer.OrdinalIgnoreCase))) { return Commands.Help(outwriter, setup, cargs, options, filter); } diff --git a/Duplicati/CommandLine/SecretTool/Program.cs b/Duplicati/CommandLine/SecretTool/Program.cs index 8148b2278..fb59dba26 100644 --- a/Duplicati/CommandLine/SecretTool/Program.cs +++ b/Duplicati/CommandLine/SecretTool/Program.cs @@ -25,6 +25,8 @@ using System.CommandLine.NamingConventionBinder; using System.CommandLine.Parsing; using Duplicati.Library.DynamicLoader; using Duplicati.Library.Interface; +using Duplicati.Library.Utility; +using Uri = System.Uri; namespace Duplicati.CommandLine.SecretTool; @@ -74,7 +76,9 @@ public static class Program Console.WriteLine("Exception: " + ex); context.ExitCode = 1; } - }).Build() + }) + .UseAdditionalHelpAliases() + .Build() .InvokeAsync(args); } diff --git a/Duplicati/CommandLine/ServerUtil/Program.cs b/Duplicati/CommandLine/ServerUtil/Program.cs index 8485fbc8d..2ba212806 100644 --- a/Duplicati/CommandLine/ServerUtil/Program.cs +++ b/Duplicati/CommandLine/ServerUtil/Program.cs @@ -22,6 +22,7 @@ using System.CommandLine; using System.CommandLine.Builder; using System.CommandLine.Parsing; using Duplicati.CommandLine.ServerUtil.Commands; +using Duplicati.Library.Utility; namespace Duplicati.CommandLine.ServerUtil; @@ -80,6 +81,7 @@ public static class Program await next(context); }) + .UseAdditionalHelpAliases() .Build() .InvokeAsync(args); } diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs index de14695d5..6215be08a 100644 --- a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs @@ -1,22 +1,22 @@ -// Copyright (C) 2025, The Duplicati Team -// https://duplicati.com, hello@duplicati.com -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// Copyright (C) 2025, The Duplicati Team +// https://duplicati.com, hello@duplicati.com +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; @@ -26,7 +26,9 @@ using System.Linq; using System.Net; using Duplicati.Library.Interface; using Duplicati.Library.RestAPI; +using Duplicati.Library.Utility; using Duplicati.Server; +using Uri = System.Uri; namespace Duplicati.GUI.TrayIcon { @@ -67,8 +69,6 @@ namespace Duplicati.GUI.TrayIcon private static bool disableTrayIconLogin = false; private static bool openui = false; private static Uri serverURL = new Uri(DEFAULT_HOSTURL); - - public static string BrowserCommand { get { return _browser_command; } } public static Server.Database.Connection databaseConnection = null; @@ -84,16 +84,9 @@ namespace Duplicati.GUI.TrayIcon if (OperatingSystem.IsWindows() && !Library.Utility.Utility.ParseBoolOption(options, DETACHED_PROCESS)) Library.Utility.Win32.AttachConsole(Library.Utility.Win32.ATTACH_PARENT_PROCESS); - - foreach (string s in args) - if ( - s.Equals("help", StringComparison.OrdinalIgnoreCase) || - s.Equals("/help", StringComparison.OrdinalIgnoreCase) || - s.Equals("usage", StringComparison.OrdinalIgnoreCase) || - s.Equals("/usage", StringComparison.OrdinalIgnoreCase)) - options["help"] = ""; - - if (options.ContainsKey("help")) + + if (HelpOptionExtensions.AlternativeHelpStrings.Any(helpString => + args.Contains(helpString, StringComparer.OrdinalIgnoreCase))) { Console.WriteLine("Supported commandline arguments:"); Console.WriteLine(); diff --git a/Duplicati/Library/Snapshots/Program.cs b/Duplicati/Library/Snapshots/Program.cs index 38f7f1fa1..b1fd61386 100644 --- a/Duplicati/Library/Snapshots/Program.cs +++ b/Duplicati/Library/Snapshots/Program.cs @@ -1,28 +1,30 @@ -// Copyright (C) 2025, The Duplicati Team -// https://duplicati.com, hello@duplicati.com -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// Copyright (C) 2025, The Duplicati Team +// https://duplicati.com, hello@duplicati.com +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; +using System.Linq; using Duplicati.Library.AutoUpdater; using Duplicati.Library.Common; +using Duplicati.Library.Utility; namespace Duplicati.Library.Snapshots { @@ -72,7 +74,8 @@ namespace Duplicati.Library.Snapshots if (args.Count == 0) args = new List { System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) }; - if (args.Count != 1) + if (args.Count != 1 || HelpOptionExtensions.AlternativeHelpStrings.Any(helpString => + args.Contains(helpString, StringComparer.OrdinalIgnoreCase))) { Console.WriteLine(@$"Usage: {PackageHelper.GetExecutableName(PackageHelper.NamedExecutable.Snapshots)} [test-folder] diff --git a/Duplicati/Library/Utility/Duplicati.Library.Utility.csproj b/Duplicati/Library/Utility/Duplicati.Library.Utility.csproj index cc7364776..8e8400f4d 100644 --- a/Duplicati/Library/Utility/Duplicati.Library.Utility.csproj +++ b/Duplicati/Library/Utility/Duplicati.Library.Utility.csproj @@ -10,6 +10,7 @@ + diff --git a/Duplicati/Library/Utility/HelpOptionExtensions.cs b/Duplicati/Library/Utility/HelpOptionExtensions.cs new file mode 100644 index 000000000..91f4e87eb --- /dev/null +++ b/Duplicati/Library/Utility/HelpOptionExtensions.cs @@ -0,0 +1,50 @@ +// Copyright (C) 2025, The Duplicati Team +// https://duplicati.com, hello@duplicati.com +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System.CommandLine.Builder; +using System.Linq; + +namespace Duplicati.Library.Utility; + +public static class HelpOptionExtensions +{ + /// + /// Aliases to support for help command + /// + public static readonly string[] AlternativeHelpStrings = ["help", "/help","--help", "usage", "/usage", "--usage", "/h", "-h"]; + + /// + /// Adds the following aliases: "help", "/help", "--usage", "/usage". + /// These aliases will work the same way as the default help options (-h, --help). + /// + /// The command line builder to extend. + /// The command line builder with additional help aliases. + public static CommandLineBuilder UseAdditionalHelpAliases(this CommandLineBuilder builder) + { + var rootHelpOption = builder.Command.Options.FirstOrDefault(o => o.GetType().Name == "HelpOption"); + if (rootHelpOption == null) return builder; + foreach (var alias in AlternativeHelpStrings) + if (!rootHelpOption.Aliases.Contains(alias)) + rootHelpOption.AddAlias(alias); + + return builder; + } +} \ No newline at end of file diff --git a/Duplicati/Server/Program.cs b/Duplicati/Server/Program.cs index 498f4c138..2a28b4bb6 100644 --- a/Duplicati/Server/Program.cs +++ b/Duplicati/Server/Program.cs @@ -1,22 +1,22 @@ -// Copyright (C) 2025, The Duplicati Team -// https://duplicati.com, hello@duplicati.com -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// Copyright (C) 2025, The Duplicati Team +// https://duplicati.com, hello@duplicati.com +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; @@ -44,11 +44,7 @@ namespace Duplicati.Server { public class Program { - - private static readonly string[] AlternativeHelpStrings = ["help", "/help", "usage", "/usage", "--help"]; - private static readonly string[] ParameterFileOptionStrings = ["parameters-file", "parameterfile"]; - private const string PING_PONG_KEEPALIVE_OPTION = "ping-pong-keepalive"; private const string WINDOWS_EVENTLOG_OPTION = "windows-eventlog"; private const string WINDOWS_EVENTLOG_LEVEL_OPTION = "windows-eventlog-level"; @@ -227,7 +223,7 @@ namespace Duplicati.Server var commandlineOptions = optionsWithFilter.Item1; var filter = optionsWithFilter.Item2; - if (_args.Select(s => s.ToLower()).Intersect(AlternativeHelpStrings.Select(x => x.ToLower())).Any()) + if (HelpOptionExtensions.AlternativeHelpStrings.Any(helpString => args.Contains(helpString, StringComparer.OrdinalIgnoreCase))) { return ShowHelp(writeToConsoleOnException); } diff --git a/Duplicati/WindowsService/Program.cs b/Duplicati/WindowsService/Program.cs index ed0762f8b..4c37b643e 100644 --- a/Duplicati/WindowsService/Program.cs +++ b/Duplicati/WindowsService/Program.cs @@ -1,22 +1,22 @@ -// Copyright (C) 2025, The Duplicati Team -// https://duplicati.com, hello@duplicati.com -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// Copyright (C) 2025, The Duplicati Team +// https://duplicati.com, hello@duplicati.com +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; using System.IO; @@ -24,11 +24,13 @@ using System.Linq; using System.Reflection; using System.ServiceProcess; using Duplicati.Library.AutoUpdater; +using Duplicati.Library.Utility; namespace Duplicati.WindowsService { public class Program { + [STAThread] public static int Main(string[] args) { @@ -40,7 +42,7 @@ namespace Duplicati.WindowsService var uninstall = args != null && args.Any(x => string.Equals("uninstall", x, StringComparison.OrdinalIgnoreCase)); var install_agent = args != null && args.Any(x => string.Equals("install-agent", x, StringComparison.OrdinalIgnoreCase) || string.Equals("install-only-agent", x, StringComparison.OrdinalIgnoreCase)); var uninstall_agent = args != null && args.Any(x => string.Equals("uninstall-agent", x, StringComparison.OrdinalIgnoreCase)); - var help = args != null && args.Any(x => string.Equals("help", x, StringComparison.OrdinalIgnoreCase)); + var help = args != null && HelpOptionExtensions.AlternativeHelpStrings.Any(helpString => args.Contains(helpString, StringComparer.OrdinalIgnoreCase));; if (help) { From cee1b8428d94ed4cc402d3c4b95947abe62ae153 Mon Sep 17 00:00:00 2001 From: "Marcelo C." Date: Tue, 21 Jan 2025 12:45:53 -0300 Subject: [PATCH 2/2] Centralizing the check on Helper function. --- Duplicati/CommandLine/BackendTester/Program.cs | 3 +-- Duplicati/CommandLine/BackendTool/Program.cs | 3 +-- Duplicati/CommandLine/CLI/Program.cs | 3 +-- Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs | 3 +-- Duplicati/Library/Snapshots/Program.cs | 3 +-- Duplicati/Library/Utility/HelpOptionExtensions.cs | 11 ++++++++++- Duplicati/Server/Program.cs | 2 +- Duplicati/WindowsService/Program.cs | 2 +- 8 files changed, 17 insertions(+), 13 deletions(-) diff --git a/Duplicati/CommandLine/BackendTester/Program.cs b/Duplicati/CommandLine/BackendTester/Program.cs index 0af39f96e..9332386d8 100644 --- a/Duplicati/CommandLine/BackendTester/Program.cs +++ b/Duplicati/CommandLine/BackendTester/Program.cs @@ -82,8 +82,7 @@ namespace Duplicati.CommandLine.BackendTester List args = new List(_args); Dictionary options = Library.Utility.CommandLineParser.ExtractOptions(args); - if (args.Count != 1 || HelpOptionExtensions.AlternativeHelpStrings.Any(helpString => - args.Contains(helpString, StringComparer.OrdinalIgnoreCase))) + if (args.Count != 1 || HelpOptionExtensions.IsArgumentAnyHelpString(args)) { Console.WriteLine("Usage: ://:@"); Console.WriteLine("Example: ftp://user:pass@server/folder"); diff --git a/Duplicati/CommandLine/BackendTool/Program.cs b/Duplicati/CommandLine/BackendTool/Program.cs index eaed7fbf1..46782329d 100644 --- a/Duplicati/CommandLine/BackendTool/Program.cs +++ b/Duplicati/CommandLine/BackendTool/Program.cs @@ -74,8 +74,7 @@ namespace Duplicati.CommandLine.BackendTool } - if (args.Count < 2 || HelpOptionExtensions.AlternativeHelpStrings.Any(helpString => - args.Contains(helpString, StringComparer.OrdinalIgnoreCase)) || command == null) + if (args.Count < 2 || HelpOptionExtensions.IsArgumentAnyHelpString(args) || command == null) { if (command == null && args.Count >= 2) { diff --git a/Duplicati/CommandLine/CLI/Program.cs b/Duplicati/CommandLine/CLI/Program.cs index 0fc00eaff..adea1cc01 100644 --- a/Duplicati/CommandLine/CLI/Program.cs +++ b/Duplicati/CommandLine/CLI/Program.cs @@ -165,8 +165,7 @@ namespace Duplicati.CommandLine } // Probe for "help" to avoid extra processing - if (cargs.Count == 0 || HelpOptionExtensions.AlternativeHelpStrings.Any(helpString => - cargs.Contains(helpString, StringComparer.OrdinalIgnoreCase))) + if (cargs.Count == 0 || HelpOptionExtensions.IsArgumentAnyHelpString(cargs)) { return Commands.Help(outwriter, setup, cargs, options, filter); } diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs index 6215be08a..0e7ca2af6 100644 --- a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs @@ -85,8 +85,7 @@ namespace Duplicati.GUI.TrayIcon if (OperatingSystem.IsWindows() && !Library.Utility.Utility.ParseBoolOption(options, DETACHED_PROCESS)) Library.Utility.Win32.AttachConsole(Library.Utility.Win32.ATTACH_PARENT_PROCESS); - if (HelpOptionExtensions.AlternativeHelpStrings.Any(helpString => - args.Contains(helpString, StringComparer.OrdinalIgnoreCase))) + if (HelpOptionExtensions.IsArgumentAnyHelpString(args)) { Console.WriteLine("Supported commandline arguments:"); Console.WriteLine(); diff --git a/Duplicati/Library/Snapshots/Program.cs b/Duplicati/Library/Snapshots/Program.cs index b1fd61386..5f39417e9 100644 --- a/Duplicati/Library/Snapshots/Program.cs +++ b/Duplicati/Library/Snapshots/Program.cs @@ -74,8 +74,7 @@ namespace Duplicati.Library.Snapshots if (args.Count == 0) args = new List { System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) }; - if (args.Count != 1 || HelpOptionExtensions.AlternativeHelpStrings.Any(helpString => - args.Contains(helpString, StringComparer.OrdinalIgnoreCase))) + if (args.Count != 1 || HelpOptionExtensions.IsArgumentAnyHelpString(args)) { Console.WriteLine(@$"Usage: {PackageHelper.GetExecutableName(PackageHelper.NamedExecutable.Snapshots)} [test-folder] diff --git a/Duplicati/Library/Utility/HelpOptionExtensions.cs b/Duplicati/Library/Utility/HelpOptionExtensions.cs index 91f4e87eb..209adfab3 100644 --- a/Duplicati/Library/Utility/HelpOptionExtensions.cs +++ b/Duplicati/Library/Utility/HelpOptionExtensions.cs @@ -19,6 +19,8 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System; +using System.Collections.Generic; using System.CommandLine.Builder; using System.Linq; @@ -29,7 +31,7 @@ public static class HelpOptionExtensions /// /// Aliases to support for help command /// - public static readonly string[] AlternativeHelpStrings = ["help", "/help","--help", "usage", "/usage", "--usage", "/h", "-h"]; + private static readonly string[] AlternativeHelpStrings = ["help", "/help","--help", "usage", "/usage", "--usage", "/h", "-h"]; /// /// Adds the following aliases: "help", "/help", "--usage", "/usage". @@ -47,4 +49,11 @@ public static class HelpOptionExtensions return builder; } + + /// + /// Returns true if any of the arguments are any of the help strings. + /// + /// Arguments + public static bool IsArgumentAnyHelpString(IEnumerable args) + => args != null && AlternativeHelpStrings.Any(x => args.Contains(x, StringComparer.OrdinalIgnoreCase)); } \ No newline at end of file diff --git a/Duplicati/Server/Program.cs b/Duplicati/Server/Program.cs index 2a28b4bb6..310bf89eb 100644 --- a/Duplicati/Server/Program.cs +++ b/Duplicati/Server/Program.cs @@ -223,7 +223,7 @@ namespace Duplicati.Server var commandlineOptions = optionsWithFilter.Item1; var filter = optionsWithFilter.Item2; - if (HelpOptionExtensions.AlternativeHelpStrings.Any(helpString => args.Contains(helpString, StringComparer.OrdinalIgnoreCase))) + if (HelpOptionExtensions.IsArgumentAnyHelpString(args)) { return ShowHelp(writeToConsoleOnException); } diff --git a/Duplicati/WindowsService/Program.cs b/Duplicati/WindowsService/Program.cs index 4c37b643e..21b71782a 100644 --- a/Duplicati/WindowsService/Program.cs +++ b/Duplicati/WindowsService/Program.cs @@ -42,7 +42,7 @@ namespace Duplicati.WindowsService var uninstall = args != null && args.Any(x => string.Equals("uninstall", x, StringComparison.OrdinalIgnoreCase)); var install_agent = args != null && args.Any(x => string.Equals("install-agent", x, StringComparison.OrdinalIgnoreCase) || string.Equals("install-only-agent", x, StringComparison.OrdinalIgnoreCase)); var uninstall_agent = args != null && args.Any(x => string.Equals("uninstall-agent", x, StringComparison.OrdinalIgnoreCase)); - var help = args != null && HelpOptionExtensions.AlternativeHelpStrings.Any(helpString => args.Contains(helpString, StringComparer.OrdinalIgnoreCase));; + var help = args != null && HelpOptionExtensions.IsArgumentAnyHelpString(args); if (help) {