From f7e8573daeba7532aa0bb134c3e1919bf3e0c3d6 Mon Sep 17 00:00:00 2001 From: hrumhurum Date: Tue, 17 Aug 2021 14:55:23 +0300 Subject: [PATCH 1/6] Fixed issue with tests when running from a folder containing space. --- src/Dotnet.Script.Tests/ScriptTestRunner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dotnet.Script.Tests/ScriptTestRunner.cs b/src/Dotnet.Script.Tests/ScriptTestRunner.cs index ebb1d0d7..b7702f2d 100644 --- a/src/Dotnet.Script.Tests/ScriptTestRunner.cs +++ b/src/Dotnet.Script.Tests/ScriptTestRunner.cs @@ -88,7 +88,7 @@ private string GetDotnetScriptArguments(string arguments) configuration = "Release"; #endif - var allArgs = $"exec {Path.Combine(Directory.GetCurrentDirectory(), "..", "..", "..", "..", "Dotnet.Script", "bin", configuration, _scriptEnvironment.TargetFramework, "dotnet-script.dll")} {arguments}"; + var allArgs = $"exec \"{Path.Combine(Directory.GetCurrentDirectory(), "..", "..", "..", "..", "Dotnet.Script", "bin", configuration, _scriptEnvironment.TargetFramework, "dotnet-script.dll")}\" {arguments}"; return allArgs; } From 1c715d4b8f5a1ce14c4b5b8041cf76531bcb1111 Mon Sep 17 00:00:00 2001 From: hrumhurum Date: Tue, 17 Aug 2021 15:54:48 +0300 Subject: [PATCH 2/6] Fixed test issues when running from a folder containing space. --- src/Dotnet.Script.Tests/PackageSourceTests.cs | 4 ++-- src/Dotnet.Script.Tests/ScriptTestRunner.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Dotnet.Script.Tests/PackageSourceTests.cs b/src/Dotnet.Script.Tests/PackageSourceTests.cs index c4668213..a7c30736 100644 --- a/src/Dotnet.Script.Tests/PackageSourceTests.cs +++ b/src/Dotnet.Script.Tests/PackageSourceTests.cs @@ -17,7 +17,7 @@ public void ShouldHandleSpecifyingPackageSource() { var fixture = "ScriptPackage/WithNoNuGetConfig"; var pathToScriptPackages = ScriptPackagesFixture.GetPathToPackagesFolder(); - var result = ScriptTestRunner.Default.ExecuteFixture(fixture, $"--no-cache -s {pathToScriptPackages}"); + var result = ScriptTestRunner.Default.ExecuteFixture(fixture, $"--no-cache -s \"{pathToScriptPackages}\""); Assert.Contains("Hello", result.output); Assert.Equal(0, result.exitCode); } @@ -27,7 +27,7 @@ public void ShouldHandleSpecifyingPackageSourceWhenEvaluatingCode() { var pathToScriptPackages = ScriptPackagesFixture.GetPathToPackagesFolder(); var code = @"#load \""nuget:ScriptPackageWithMainCsx,1.0.0\"" SayHello();"; - var result = ScriptTestRunner.Default.Execute($"--no-cache -s {pathToScriptPackages} eval \"{code}\""); + var result = ScriptTestRunner.Default.Execute($"--no-cache -s \"{pathToScriptPackages}\" eval \"{code}\""); Assert.Contains("Hello", result.output); Assert.Equal(0, result.exitCode); } diff --git a/src/Dotnet.Script.Tests/ScriptTestRunner.cs b/src/Dotnet.Script.Tests/ScriptTestRunner.cs index b7702f2d..bd6ece3f 100644 --- a/src/Dotnet.Script.Tests/ScriptTestRunner.cs +++ b/src/Dotnet.Script.Tests/ScriptTestRunner.cs @@ -38,7 +38,7 @@ public int ExecuteInProcess(string arguments = null) public (string output, int exitCode) ExecuteFixture(string fixture, string arguments = null, string workingDirectory = null) { var pathToFixture = TestPathUtils.GetPathToTestFixture(fixture); - var result = ProcessHelper.RunAndCaptureOutput("dotnet", GetDotnetScriptArguments($"{pathToFixture} {arguments}"), workingDirectory); + var result = ProcessHelper.RunAndCaptureOutput("dotnet", GetDotnetScriptArguments($"\"{pathToFixture}\" {arguments}"), workingDirectory); return result; } @@ -46,7 +46,7 @@ public int ExecuteInProcess(string arguments = null) { var pathToScriptPackageFixtures = TestPathUtils.GetPathToTestFixtureFolder("ScriptPackage"); var pathToFixture = Path.Combine(pathToScriptPackageFixtures, fixture, $"{fixture}.csx"); - return ProcessHelper.RunAndCaptureOutput("dotnet", GetDotnetScriptArguments($"{pathToFixture} {arguments}"), workingDirectory); + return ProcessHelper.RunAndCaptureOutput("dotnet", GetDotnetScriptArguments($"\"{pathToFixture}\" {arguments}"), workingDirectory); } public int ExecuteFixtureInProcess(string fixture, string arguments = null) From d48d429834100a389cac57df25e85a296ae2b953 Mon Sep 17 00:00:00 2001 From: hrumhurum Date: Tue, 17 Aug 2021 16:44:17 +0300 Subject: [PATCH 3/6] Fixed test issues when running from a folder containing space. --- src/Dotnet.Script.Tests/ScriptPackagesFixture.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Dotnet.Script.Tests/ScriptPackagesFixture.cs b/src/Dotnet.Script.Tests/ScriptPackagesFixture.cs index 1dadbdc2..2e635fcb 100644 --- a/src/Dotnet.Script.Tests/ScriptPackagesFixture.cs +++ b/src/Dotnet.Script.Tests/ScriptPackagesFixture.cs @@ -43,12 +43,12 @@ private void BuildScriptPackages() if (_scriptEnvironment.IsWindows) { command = pathtoNuget430; - var result = ProcessHelper.RunAndCaptureOutput(command, $"pack {specFile} -OutputDirectory {pathToPackagesOutputFolder}"); + var result = ProcessHelper.RunAndCaptureOutput(command, $"pack \"{specFile}\" -OutputDirectory \"{pathToPackagesOutputFolder}\""); } else { command = "mono"; - var result = ProcessHelper.RunAndCaptureOutput(command, $"{pathtoNuget430} pack {specFile} -OutputDirectory {pathToPackagesOutputFolder}"); + var result = ProcessHelper.RunAndCaptureOutput(command, $"\"{pathtoNuget430}\" pack \"{specFile}\" -OutputDirectory \"{pathToPackagesOutputFolder}\""); } } From eece2a4dbe9ac0d4d8764e9e94929fe0ce635c43 Mon Sep 17 00:00:00 2001 From: hrumhurum Date: Tue, 17 Aug 2021 17:04:58 +0300 Subject: [PATCH 4/6] Fixed issue in DotNetRestorer with NuGet sources containing space. --- src/Dotnet.Script.DependencyModel/Context/DotnetRestorer.cs | 3 ++- .../Dotnet.Script.DependencyModel.csproj | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Dotnet.Script.DependencyModel/Context/DotnetRestorer.cs b/src/Dotnet.Script.DependencyModel/Context/DotnetRestorer.cs index 8b8a9f4a..4b028e23 100644 --- a/src/Dotnet.Script.DependencyModel/Context/DotnetRestorer.cs +++ b/src/Dotnet.Script.DependencyModel/Context/DotnetRestorer.cs @@ -2,6 +2,7 @@ using Dotnet.Script.DependencyModel.Logging; using Dotnet.Script.DependencyModel.Process; using Dotnet.Script.DependencyModel.ProjectSystem; +using Gapotchenko.FX.Diagnostics; using System; using System.IO; using System.Linq; @@ -44,7 +45,7 @@ string CreatePackageSourcesArguments() { return packageSources.Length == 0 ? string.Empty - : packageSources.Select(s => $"-s {s}") + : packageSources.Select(s => $"-s {CommandLine.EscapeArgument(s)}") .Aggregate((current, next) => $"{current} {next}"); } diff --git a/src/Dotnet.Script.DependencyModel/Dotnet.Script.DependencyModel.csproj b/src/Dotnet.Script.DependencyModel/Dotnet.Script.DependencyModel.csproj index c1141c7b..ecb2a623 100644 --- a/src/Dotnet.Script.DependencyModel/Dotnet.Script.DependencyModel.csproj +++ b/src/Dotnet.Script.DependencyModel/Dotnet.Script.DependencyModel.csproj @@ -32,6 +32,7 @@ + From 818d4958020dcc98fd4405ba55e0f830f2a1ec16 Mon Sep 17 00:00:00 2001 From: hrumhurum Date: Tue, 17 Aug 2021 17:14:04 +0300 Subject: [PATCH 5/6] Fixed all remaining test issues when running from a folder containing a space character in its name. --- src/Dotnet.Script.Tests/ScriptExecutionTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Dotnet.Script.Tests/ScriptExecutionTests.cs b/src/Dotnet.Script.Tests/ScriptExecutionTests.cs index 62c84cb8..230bcd7d 100644 --- a/src/Dotnet.Script.Tests/ScriptExecutionTests.cs +++ b/src/Dotnet.Script.Tests/ScriptExecutionTests.cs @@ -3,6 +3,7 @@ using System.Text; using Dotnet.Script.DependencyModel.Environment; using Dotnet.Script.Shared.Tests; +using Gapotchenko.FX.Diagnostics; using Xunit; using Xunit.Abstractions; @@ -139,7 +140,7 @@ public void ShouldHandleIssue181() [Fact] public void ShouldHandleIssue189() { - var result = ScriptTestRunner.Default.Execute(Path.Combine(TestPathUtils.GetPathToTestFixtureFolder("Issue189"), "SomeFolder", "Script.csx")); + var result = ScriptTestRunner.Default.Execute(CommandLine.Build(Path.Combine(TestPathUtils.GetPathToTestFixtureFolder("Issue189"), "SomeFolder", "Script.csx"))); Assert.Contains("Newtonsoft.Json.JsonConvert", result.output); } From 467eafdc959e08748b370b3297f754a9101db921 Mon Sep 17 00:00:00 2001 From: hrumhurum Date: Tue, 17 Aug 2021 17:58:39 +0300 Subject: [PATCH 6/6] Standalone implementation of a command line escape logic. --- .../Context/DotnetRestorer.cs | 2 +- .../Dotnet.Script.DependencyModel.csproj | 1 - .../Internal/CommandLine.cs | 93 +++++++++++++++++++ .../ScriptExecutionTests.cs | 3 +- 4 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 src/Dotnet.Script.DependencyModel/Internal/CommandLine.cs diff --git a/src/Dotnet.Script.DependencyModel/Context/DotnetRestorer.cs b/src/Dotnet.Script.DependencyModel/Context/DotnetRestorer.cs index 4b028e23..b3bcf621 100644 --- a/src/Dotnet.Script.DependencyModel/Context/DotnetRestorer.cs +++ b/src/Dotnet.Script.DependencyModel/Context/DotnetRestorer.cs @@ -1,8 +1,8 @@ using Dotnet.Script.DependencyModel.Environment; +using Dotnet.Script.DependencyModel.Internal; using Dotnet.Script.DependencyModel.Logging; using Dotnet.Script.DependencyModel.Process; using Dotnet.Script.DependencyModel.ProjectSystem; -using Gapotchenko.FX.Diagnostics; using System; using System.IO; using System.Linq; diff --git a/src/Dotnet.Script.DependencyModel/Dotnet.Script.DependencyModel.csproj b/src/Dotnet.Script.DependencyModel/Dotnet.Script.DependencyModel.csproj index ecb2a623..c1141c7b 100644 --- a/src/Dotnet.Script.DependencyModel/Dotnet.Script.DependencyModel.csproj +++ b/src/Dotnet.Script.DependencyModel/Dotnet.Script.DependencyModel.csproj @@ -32,7 +32,6 @@ - diff --git a/src/Dotnet.Script.DependencyModel/Internal/CommandLine.cs b/src/Dotnet.Script.DependencyModel/Internal/CommandLine.cs new file mode 100644 index 00000000..d7a037ba --- /dev/null +++ b/src/Dotnet.Script.DependencyModel/Internal/CommandLine.cs @@ -0,0 +1,93 @@ +using System; +using System.Text; +using System.Text.RegularExpressions; + +namespace Dotnet.Script.DependencyModel.Internal +{ + /// + /// + /// Performs operations on instances that contain command line information. + /// + /// + /// Tip: a ready-to-use package with this functionality is available at https://www.nuget.org/packages/Gapotchenko.FX.Diagnostics.CommandLine. + /// + /// + /// + /// Available + /// + static class CommandLine + { + /// + /// Escapes and optionally quotes a command line argument. + /// + /// The command line argument. + /// The escaped and optionally quoted command line argument. + public static string EscapeArgument(string value) + { + if (value == null) + return null; + + int length = value.Length; + if (length == 0) + return string.Empty; + + var sb = new StringBuilder(); + Escape.AppendQuotedText(sb, value); + + if (sb.Length == length) + return value; + + return sb.ToString(); + } + + static class Escape + { + public static void AppendQuotedText(StringBuilder sb, string text) + { + bool quotingRequired = IsQuotingRequired(text); + if (quotingRequired) + sb.Append('"'); + + int numberOfQuotes = 0; + for (int i = 0; i < text.Length; i++) + { + if (text[i] == '"') + numberOfQuotes++; + } + + if (numberOfQuotes > 0) + { + if ((numberOfQuotes % 2) != 0) + throw new Exception("Command line parameter cannot contain an odd number of double quotes."); + text = text.Replace("\\\"", "\\\\\"").Replace("\"", "\\\""); + } + + sb.Append(text); + + if (quotingRequired && text.EndsWith("\\")) + sb.Append('\\'); + + if (quotingRequired) + sb.Append('"'); + } + + static bool IsQuotingRequired(string parameter) => + !AllowedUnquotedRegex.IsMatch(parameter) || + DefinitelyNeedQuotesRegex.IsMatch(parameter); + + static Regex m_CachedAllowedUnquotedRegex; + + static Regex AllowedUnquotedRegex => + m_CachedAllowedUnquotedRegex ??= new Regex( + @"^[a-z\\/:0-9\._\-+=]*$", + RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + + static Regex m_CachedDefinitelyNeedQuotesRegex; + + static Regex DefinitelyNeedQuotesRegex => + m_CachedDefinitelyNeedQuotesRegex ??= new Regex( + "[|><\\s,;\"]+", + RegexOptions.CultureInvariant); + } + } +} diff --git a/src/Dotnet.Script.Tests/ScriptExecutionTests.cs b/src/Dotnet.Script.Tests/ScriptExecutionTests.cs index 230bcd7d..f13fd397 100644 --- a/src/Dotnet.Script.Tests/ScriptExecutionTests.cs +++ b/src/Dotnet.Script.Tests/ScriptExecutionTests.cs @@ -3,7 +3,6 @@ using System.Text; using Dotnet.Script.DependencyModel.Environment; using Dotnet.Script.Shared.Tests; -using Gapotchenko.FX.Diagnostics; using Xunit; using Xunit.Abstractions; @@ -140,7 +139,7 @@ public void ShouldHandleIssue181() [Fact] public void ShouldHandleIssue189() { - var result = ScriptTestRunner.Default.Execute(CommandLine.Build(Path.Combine(TestPathUtils.GetPathToTestFixtureFolder("Issue189"), "SomeFolder", "Script.csx"))); + var result = ScriptTestRunner.Default.Execute($"\"{Path.Combine(TestPathUtils.GetPathToTestFixtureFolder("Issue189"), "SomeFolder", "Script.csx")}\""); Assert.Contains("Newtonsoft.Json.JsonConvert", result.output); }