|
| 1 | +// Copyright (c) Ubiquity.NET Contributors. All rights reserved. |
| 2 | +// Licensed under the MIT license. See the LICENSE.md file in the project root for full license information. |
| 3 | + |
| 4 | +using System; |
| 5 | +using System.Linq; |
| 6 | + |
| 7 | +namespace Ubiquity.CommandlineParsing |
| 8 | +{ |
| 9 | + /// <summary>Provides environment extensions for handling platform differences</summary> |
| 10 | + public static class EnvironmentEx |
| 11 | + { |
| 12 | + /// <summary>Gets the raw unparsed command line (or as close to it as is possible for the given runtime/platform</summary> |
| 13 | + /// <remarks> |
| 14 | + /// <para>On Desktop .NET 4.7 and earlier <see cref="Environment.CommandLine"/> provides the full command line without any |
| 15 | + /// funky escape character processing. But the args passed to main or returned from <see cref="Environment.GetCommandLineArgs()"/> |
| 16 | + /// have extra escape character processing. This extra processing is very problematic as it is unique to .NET apps |
| 17 | + /// (C and C++ apps don't have this). If the command line `foo.exe "abc\de f\"` is processed with escaping then the |
| 18 | + /// result the app see is `abc\de f"` (Note the inclusion of the trailing quote character), which is clearly not the |
| 19 | + /// path the user intended to provide. This has always been an annoyance for .NET developers but was easily worked around |
| 20 | + /// by not using the args to Main() and instead getting at the full args via Environment.CommandLine and parsing that using |
| 21 | + /// whatever rules the app wants to support for args.</para> |
| 22 | + /// <para>Unfortunately with .NET Core things behave very differently. <see cref="Environment.CommandLine"/> is no longer |
| 23 | + /// the unprocessed command line and instead it is effectively a space delimited join of Environment.GetCommandLineArgs(). |
| 24 | + /// Meaning that is has all the .NET Specific escaping applied and there is no platform independent means of getting at |
| 25 | + /// the unprocessed args.</para> |
| 26 | + /// <para>Thus, this implementation is forced to implement the only viable cross platform approach by doing a space delimited |
| 27 | + /// join of <see cref="Environment.GetCommandLineArgs()"/> with all it's wonky escaping rules.</para> |
| 28 | + /// </remarks> |
| 29 | + public static string CommandLine |
| 30 | + { |
| 31 | + get |
| 32 | + { |
| 33 | + // On .NET Core - no option exists to get the original command line, it isn't really possible to fully reverse |
| 34 | + // the escaping as the process is lossy. e.g. `"foo\"` -> `foo"`, and `foo\"`-> `foo"` with no way to know if |
| 35 | + // the opening quote was present. For a value with whitespace it can be inferred but otherwise it can't be known |
| 36 | + // which presents a problem as reversing the escaping could creates quotes that are unmatched. |
| 37 | + return string.Join( " ", Environment.GetCommandLineArgs( ).Skip( 1 ).Select( Requote ) ); |
| 38 | + } |
| 39 | + } |
| 40 | + |
| 41 | + private static string Requote( string arg ) |
| 42 | + { |
| 43 | + if( arg.Any( Char.IsWhiteSpace ) ) |
| 44 | + { |
| 45 | + return $"\"{arg}\""; |
| 46 | + } |
| 47 | + |
| 48 | + return arg; |
| 49 | + } |
| 50 | + } |
| 51 | +} |
0 commit comments