diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java index e6b75db6e82e36..0ceebc064a9e5e 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java @@ -1048,7 +1048,7 @@ private static FileSystem defaultFileSystemImplementation() private static SubprocessFactory subprocessFactoryImplementation() { if (!"0".equals(System.getProperty("io.bazel.EnableJni")) && OS.getCurrent() == OS.WINDOWS) { - return WindowsSubprocessFactory.INSTANCE; + return new WindowsSubprocessFactory(false); } else { return JavaSubprocessFactory.INSTANCE; } @@ -1155,7 +1155,7 @@ private static BlazeRuntime newRuntime(Iterable blazeModules, List< "No module set the default hash function.", ExitCode.BLAZE_INTERNAL_ERROR, e); } Path.setFileSystemForSerialization(fs); - SubprocessBuilder.setSubprocessFactory(subprocessFactoryImplementation()); + SubprocessBuilder.setDefaultSubprocessFactory(subprocessFactoryImplementation()); Path outputUserRootPath = fs.getPath(outputUserRoot); Path installBasePath = fs.getPath(installBase); diff --git a/src/main/java/com/google/devtools/build/lib/shell/SubprocessBuilder.java b/src/main/java/com/google/devtools/build/lib/shell/SubprocessBuilder.java index d3ce1ee21b2f5a..e962e14993ec61 100644 --- a/src/main/java/com/google/devtools/build/lib/shell/SubprocessBuilder.java +++ b/src/main/java/com/google/devtools/build/lib/shell/SubprocessBuilder.java @@ -41,6 +41,7 @@ public enum StreamAction { STREAM } + private final SubprocessFactory factory; private ImmutableList argv; private ImmutableMap env; private StreamAction stdoutAction; @@ -51,15 +52,20 @@ public enum StreamAction { private long timeoutMillis; private boolean redirectErrorStream; - static SubprocessFactory factory = JavaSubprocessFactory.INSTANCE; + static SubprocessFactory defaultFactory = JavaSubprocessFactory.INSTANCE; - public static void setSubprocessFactory(SubprocessFactory factory) { - SubprocessBuilder.factory = factory; + public static void setDefaultSubprocessFactory(SubprocessFactory factory) { + SubprocessBuilder.defaultFactory = factory; } public SubprocessBuilder() { + this(defaultFactory); + } + + public SubprocessBuilder(SubprocessFactory factory) { stdoutAction = StreamAction.STREAM; stderrAction = StreamAction.STREAM; + this.factory = factory; } /** diff --git a/src/main/java/com/google/devtools/build/lib/windows/WindowsSubprocessFactory.java b/src/main/java/com/google/devtools/build/lib/windows/WindowsSubprocessFactory.java index 3ed4a6de4d808e..01e75880c653f3 100644 --- a/src/main/java/com/google/devtools/build/lib/windows/WindowsSubprocessFactory.java +++ b/src/main/java/com/google/devtools/build/lib/windows/WindowsSubprocessFactory.java @@ -14,6 +14,7 @@ package com.google.devtools.build.lib.windows; +import com.google.devtools.build.lib.shell.ShellUtils; import com.google.devtools.build.lib.shell.Subprocess; import com.google.devtools.build.lib.shell.SubprocessBuilder; import com.google.devtools.build.lib.shell.SubprocessBuilder.StreamAction; @@ -31,10 +32,10 @@ * A subprocess factory that uses the Win32 API. */ public class WindowsSubprocessFactory implements SubprocessFactory { - public static final WindowsSubprocessFactory INSTANCE = new WindowsSubprocessFactory(); + private final boolean windowsStyleArgEscaping; - private WindowsSubprocessFactory() { - // Singleton + public WindowsSubprocessFactory(boolean windowsStyleArgEscaping) { + this.windowsStyleArgEscaping = windowsStyleArgEscaping; } @Override @@ -43,8 +44,7 @@ public Subprocess create(SubprocessBuilder builder) throws IOException { // DO NOT quote argv0, createProcess will do it for us. String argv0 = processArgv0(argv.get(0)); - String argvRest = - argv.size() > 1 ? WindowsProcesses.quoteCommandLine(argv.subList(1, argv.size())) : ""; + String argvRest = argv.size() > 1 ? escapeArgvRest(argv.subList(1, argv.size())) : ""; byte[] env = convertEnvToNative(builder.getEnv()); String stdoutPath = getRedirectPath(builder.getStdout(), builder.getStdoutFile()); @@ -73,7 +73,25 @@ public Subprocess create(SubprocessBuilder builder) throws IOException { builder.getTimeoutMillis()); } - public String processArgv0(String argv0) { + private String escapeArgvRest(List argv) { + if (windowsStyleArgEscaping) { + StringBuilder result = new StringBuilder(); + boolean first = true; + for (String arg : argv) { + if (first) { + first = false; + } else { + result.append(" "); + } + result.append(ShellUtils.windowsEscapeArg(arg)); + } + return result.toString(); + } else { + return WindowsProcesses.quoteCommandLine(argv); + } + } + + public static String processArgv0(String argv0) { // Normalize the path and make it Windows-style. // If argv0 is at least MAX_PATH (260 chars) long, createNativeProcess calls GetShortPathNameW // to obtain a 8dot3 name for it (thereby support long paths in CreateProcessA), but then argv0 @@ -83,10 +101,10 @@ public String processArgv0(String argv0) { // If it's not absolute, then it cannot be longer than MAX_PATH, since MAX_PATH also limits the // length of file names. PathFragment argv0fragment = PathFragment.create(argv0); - return (argv0fragment.isAbsolute()) ? argv0fragment.getPathString().replace('/', '\\') : argv0; + return argv0fragment.isAbsolute() ? argv0fragment.getPathString().replace('/', '\\') : argv0; } - private String getRedirectPath(StreamAction action, File file) { + private static String getRedirectPath(StreamAction action, File file) { switch (action) { case DISCARD: return "NUL"; // That's /dev/null on Windows @@ -102,10 +120,8 @@ private String getRedirectPath(StreamAction action, File file) { } } - /** - * Converts an environment map to the format expected in lpEnvironment by CreateProcess(). - */ - private byte[] convertEnvToNative(Map envMap) throws IOException { + /** Converts an environment map to the format expected in lpEnvironment by CreateProcess(). */ + private static byte[] convertEnvToNative(Map envMap) throws IOException { Map realEnv = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); Map systemEnv = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); if (envMap != null) { diff --git a/src/test/java/com/google/devtools/build/lib/exec/local/LocalSpawnRunnerTest.java b/src/test/java/com/google/devtools/build/lib/exec/local/LocalSpawnRunnerTest.java index 45803d74050cb9..e37e13ddc77ad5 100644 --- a/src/test/java/com/google/devtools/build/lib/exec/local/LocalSpawnRunnerTest.java +++ b/src/test/java/com/google/devtools/build/lib/exec/local/LocalSpawnRunnerTest.java @@ -279,7 +279,7 @@ public boolean isLoggable(LogRecord record) { private FileSystem setupEnvironmentForFakeExecution() { // Prevent any subprocess execution at all. - SubprocessBuilder.setSubprocessFactory(new SubprocessInterceptor()); + SubprocessBuilder.setDefaultSubprocessFactory(new SubprocessInterceptor()); resourceManager.setAvailableResources( ResourceSet.create(/*memoryMb=*/1, /*cpuUsage=*/1, /*localTestCount=*/1)); return new InMemoryFileSystem(); @@ -292,7 +292,7 @@ private FileSystem setupEnvironmentForFakeExecution() { */ @Before public final void setupEnvironmentForRealExecution() { - SubprocessBuilder.setSubprocessFactory(JavaSubprocessFactory.INSTANCE); + SubprocessBuilder.setDefaultSubprocessFactory(JavaSubprocessFactory.INSTANCE); resourceManager.setAvailableResources(LocalHostCapacity.getLocalHostCapacity()); } @@ -308,7 +308,7 @@ public void vanillaZeroExit() throws Exception { SubprocessFactory factory = mock(SubprocessFactory.class); ArgumentCaptor captor = ArgumentCaptor.forClass(SubprocessBuilder.class); when(factory.create(captor.capture())).thenReturn(new FinishedSubprocess(0)); - SubprocessBuilder.setSubprocessFactory(factory); + SubprocessBuilder.setDefaultSubprocessFactory(factory); LocalExecutionOptions options = Options.getDefaults(LocalExecutionOptions.class); options.localSigkillGraceSeconds = 456; @@ -364,7 +364,7 @@ public void testParamFiles() throws Exception { SubprocessFactory factory = mock(SubprocessFactory.class); when(factory.create(any())).thenReturn(new FinishedSubprocess(0)); - SubprocessBuilder.setSubprocessFactory(factory); + SubprocessBuilder.setDefaultSubprocessFactory(factory); LocalExecutionOptions options = Options.getDefaults(LocalExecutionOptions.class); options.localSigkillGraceSeconds = 456; @@ -414,7 +414,7 @@ public void noProcessWrapper() throws Exception { SubprocessFactory factory = mock(SubprocessFactory.class); ArgumentCaptor captor = ArgumentCaptor.forClass(SubprocessBuilder.class); when(factory.create(captor.capture())).thenReturn(new FinishedSubprocess(0)); - SubprocessBuilder.setSubprocessFactory(factory); + SubprocessBuilder.setDefaultSubprocessFactory(factory); LocalExecutionOptions options = Options.getDefaults(LocalExecutionOptions.class); options.localSigkillGraceSeconds = 456; @@ -460,7 +460,7 @@ public void nonZeroExit() throws Exception { SubprocessFactory factory = mock(SubprocessFactory.class); ArgumentCaptor captor = ArgumentCaptor.forClass(SubprocessBuilder.class); when(factory.create(captor.capture())).thenReturn(new FinishedSubprocess(3)); - SubprocessBuilder.setSubprocessFactory(factory); + SubprocessBuilder.setDefaultSubprocessFactory(factory); LocalExecutionOptions options = Options.getDefaults(LocalExecutionOptions.class); LocalSpawnRunner runner = @@ -508,7 +508,7 @@ public void processStartupThrows() throws Exception { SubprocessFactory factory = mock(SubprocessFactory.class); ArgumentCaptor captor = ArgumentCaptor.forClass(SubprocessBuilder.class); when(factory.create(captor.capture())).thenThrow(new IOException("I'm sorry, Dave")); - SubprocessBuilder.setSubprocessFactory(factory); + SubprocessBuilder.setDefaultSubprocessFactory(factory); LocalExecutionOptions options = Options.getDefaults(LocalExecutionOptions.class); LocalSpawnRunner runner = @@ -595,7 +595,7 @@ public void waitFor() throws InterruptedException { } } }); - SubprocessBuilder.setSubprocessFactory(factory); + SubprocessBuilder.setDefaultSubprocessFactory(factory); LocalExecutionOptions options = Options.getDefaults(LocalExecutionOptions.class); LocalSpawnRunner runner = @@ -627,7 +627,7 @@ public void checkPrefetchCalled() throws Exception { SubprocessFactory factory = mock(SubprocessFactory.class); when(factory.create(any())).thenReturn(new FinishedSubprocess(0)); - SubprocessBuilder.setSubprocessFactory(factory); + SubprocessBuilder.setDefaultSubprocessFactory(factory); LocalExecutionOptions options = Options.getDefaults(LocalExecutionOptions.class); LocalSpawnRunner runner = @@ -654,7 +654,7 @@ public void checkNoPrefetchCalled() throws Exception { SubprocessFactory factory = mock(SubprocessFactory.class); when(factory.create(any())).thenReturn(new FinishedSubprocess(0)); - SubprocessBuilder.setSubprocessFactory(factory); + SubprocessBuilder.setDefaultSubprocessFactory(factory); LocalExecutionOptions options = Options.getDefaults(LocalExecutionOptions.class); LocalSpawnRunner runner = @@ -684,7 +684,7 @@ public void checkLocalEnvProviderCalled() throws Exception { SubprocessFactory factory = mock(SubprocessFactory.class); when(factory.create(any())).thenReturn(new FinishedSubprocess(0)); - SubprocessBuilder.setSubprocessFactory(factory); + SubprocessBuilder.setDefaultSubprocessFactory(factory); LocalEnvProvider localEnvProvider = mock(LocalEnvProvider.class); LocalExecutionOptions options = Options.getDefaults(LocalExecutionOptions.class); @@ -723,7 +723,7 @@ public void useCorrectExtensionOnWindows() throws Exception { SubprocessFactory factory = mock(SubprocessFactory.class); ArgumentCaptor captor = ArgumentCaptor.forClass(SubprocessBuilder.class); when(factory.create(captor.capture())).thenReturn(new FinishedSubprocess(0)); - SubprocessBuilder.setSubprocessFactory(factory); + SubprocessBuilder.setDefaultSubprocessFactory(factory); LocalExecutionOptions options = Options.getDefaults(LocalExecutionOptions.class); options.localSigkillGraceSeconds = 654; @@ -973,7 +973,7 @@ public void relativePath() throws Exception { SubprocessFactory factory = mock(SubprocessFactory.class); ArgumentCaptor captor = ArgumentCaptor.forClass(SubprocessBuilder.class); when(factory.create(captor.capture())).thenReturn(new FinishedSubprocess(0)); - SubprocessBuilder.setSubprocessFactory(factory); + SubprocessBuilder.setDefaultSubprocessFactory(factory); LocalSpawnRunner runner = new TestedLocalSpawnRunner( diff --git a/src/test/java/com/google/devtools/build/lib/windows/WindowsSubprocessTest.java b/src/test/java/com/google/devtools/build/lib/windows/WindowsSubprocessTest.java index 4fb09688ef1992..d3922301802b43 100644 --- a/src/test/java/com/google/devtools/build/lib/windows/WindowsSubprocessTest.java +++ b/src/test/java/com/google/devtools/build/lib/windows/WindowsSubprocessTest.java @@ -19,6 +19,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.devtools.build.lib.shell.ShellUtils; import com.google.devtools.build.lib.shell.Subprocess; import com.google.devtools.build.lib.shell.SubprocessBuilder; import com.google.devtools.build.lib.testutil.TestSpec; @@ -26,6 +27,7 @@ import com.google.devtools.build.lib.windows.jni.WindowsProcesses; import com.google.devtools.build.runfiles.Runfiles; import java.io.File; +import java.util.function.Function; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -63,11 +65,10 @@ public void terminateProcess() throws Exception { } } - @Test - public void testSystemRootIsSetByDefault() throws Exception { - SubprocessBuilder subprocessBuilder = new SubprocessBuilder(); + private void assertSystemRootIsSetByDefault(boolean windowsStyleArgEscaping) throws Exception { + SubprocessBuilder subprocessBuilder = + new SubprocessBuilder(new WindowsSubprocessFactory(windowsStyleArgEscaping)); subprocessBuilder.setWorkingDirectory(new File(".")); - subprocessBuilder.setSubprocessFactory(WindowsSubprocessFactory.INSTANCE); subprocessBuilder.setArgv(mockBinary, "-jar", mockSubprocess, "O$SYSTEMROOT"); process = subprocessBuilder.start(); process.waitFor(); @@ -79,10 +80,19 @@ public void testSystemRootIsSetByDefault() throws Exception { } @Test - public void testSystemDriveIsSetByDefault() throws Exception { - SubprocessBuilder subprocessBuilder = new SubprocessBuilder(); + public void testSystemRootIsSetByDefaultNoWindowsStyleArgEscaping() throws Exception { + assertSystemRootIsSetByDefault(false); + } + + @Test + public void testSystemRootIsSetByDefaultWithWindowsStyleArgEscaping() throws Exception { + assertSystemRootIsSetByDefault(true); + } + + private void assertSystemDriveIsSetByDefault(boolean windowsStyleArgEscaping) throws Exception { + SubprocessBuilder subprocessBuilder = + new SubprocessBuilder(new WindowsSubprocessFactory(windowsStyleArgEscaping)); subprocessBuilder.setWorkingDirectory(new File(".")); - subprocessBuilder.setSubprocessFactory(WindowsSubprocessFactory.INSTANCE); subprocessBuilder.setArgv(mockBinary, "-jar", mockSubprocess, "O$SYSTEMDRIVE"); process = subprocessBuilder.start(); process.waitFor(); @@ -94,10 +104,19 @@ public void testSystemDriveIsSetByDefault() throws Exception { } @Test - public void testSystemRootIsSet() throws Exception { - SubprocessBuilder subprocessBuilder = new SubprocessBuilder(); + public void testSystemDriveIsSetByDefaultNoWindowsStyleArgEscaping() throws Exception { + assertSystemDriveIsSetByDefault(false); + } + + @Test + public void testSystemDriveIsSetByDefaultWithWindowsStyleArgEscaping() throws Exception { + assertSystemDriveIsSetByDefault(true); + } + + private void assertSystemRootIsSet(boolean windowsStyleArgEscaping) throws Exception { + SubprocessBuilder subprocessBuilder = + new SubprocessBuilder(new WindowsSubprocessFactory(windowsStyleArgEscaping)); subprocessBuilder.setWorkingDirectory(new File(".")); - subprocessBuilder.setSubprocessFactory(WindowsSubprocessFactory.INSTANCE); subprocessBuilder.setArgv(mockBinary, "-jar", mockSubprocess, "O$SYSTEMROOT"); // Case shouldn't matter on Windows subprocessBuilder.setEnv(ImmutableMap.of("SystemRoot", "C:\\MySystemRoot")); @@ -111,10 +130,19 @@ public void testSystemRootIsSet() throws Exception { } @Test - public void testSystemDriveIsSet() throws Exception { - SubprocessBuilder subprocessBuilder = new SubprocessBuilder(); + public void testSystemRootIsSetNoWindowsStyleArgEscaping() throws Exception { + assertSystemRootIsSet(false); + } + + @Test + public void testSystemRootIsSetWithWindowsStyleArgEscaping() throws Exception { + assertSystemRootIsSet(true); + } + + private void assertSystemDriveIsSet(boolean windowsStyleArgEscaping) throws Exception { + SubprocessBuilder subprocessBuilder = + new SubprocessBuilder(new WindowsSubprocessFactory(windowsStyleArgEscaping)); subprocessBuilder.setWorkingDirectory(new File(".")); - subprocessBuilder.setSubprocessFactory(WindowsSubprocessFactory.INSTANCE); subprocessBuilder.setArgv(mockBinary, "-jar", mockSubprocess, "O$SYSTEMDRIVE"); // Case shouldn't matter on Windows subprocessBuilder.setEnv(ImmutableMap.of("SystemDrive", "X:")); @@ -127,6 +155,16 @@ public void testSystemDriveIsSet() throws Exception { assertThat(new String(buf, UTF_8).trim()).isEqualTo("X:"); } + @Test + public void testSystemDriveIsSetNoWindowsStyleArgEscaping() throws Exception { + assertSystemDriveIsSet(false); + } + + @Test + public void testSystemDriveIsSetWithWindowsStyleArgEscaping() throws Exception { + assertSystemDriveIsSet(true); + } + /** * An argument and its command-line-escaped counterpart. * @@ -143,7 +181,9 @@ public ArgPair(String original, String escaped) { }; /** Asserts that a subprocess correctly receives command line arguments. */ - private void assertSubprocessReceivesArgsAsIntended(ArgPair... args) throws Exception { + private void assertSubprocessReceivesArgsAsIntended( + boolean windowsStyleArgEscaping, Function escaper, ArgPair... args) + throws Exception { // Look up the path of the printarg.exe utility. String printArgExe = runfiles.rlocation("io_bazel/src/test/java/com/google/devtools/build/lib/printarg.exe"); @@ -151,13 +191,12 @@ private void assertSubprocessReceivesArgsAsIntended(ArgPair... args) throws Exce for (ArgPair arg : args) { // Assert that the command-line encoding logic works as intended. - assertThat(WindowsProcesses.quoteCommandLine(ImmutableList.of(arg.original))) - .isEqualTo(arg.escaped); + assertThat(escaper.apply(arg.original)).isEqualTo(arg.escaped); // Create a separate subprocess just for this argument. - SubprocessBuilder subprocessBuilder = new SubprocessBuilder(); + SubprocessBuilder subprocessBuilder = + new SubprocessBuilder(new WindowsSubprocessFactory(windowsStyleArgEscaping)); subprocessBuilder.setWorkingDirectory(new File(".")); - subprocessBuilder.setSubprocessFactory(WindowsSubprocessFactory.INSTANCE); subprocessBuilder.setArgv(printArgExe, arg.original); process = subprocessBuilder.start(); process.waitFor(); @@ -173,8 +212,10 @@ private void assertSubprocessReceivesArgsAsIntended(ArgPair... args) throws Exce } @Test - public void testSubprocessReceivesArgsAsIntended() throws Exception { + public void testSubprocessReceivesArgsAsIntendedNoWindowsStyleArgEscaping() throws Exception { assertSubprocessReceivesArgsAsIntended( + false, + x -> WindowsProcesses.quoteCommandLine(ImmutableList.of(x)), new ArgPair("", "\"\""), new ArgPair(" ", "\" \""), new ArgPair("foo", "foo"), @@ -184,4 +225,54 @@ public void testSubprocessReceivesArgsAsIntended() throws Exception { // it fails to properly escape things like a single backslash followed by a quote, e.g. a\"b // Fix the escaping logic and add more test here. } + + @Test + public void testSubprocessReceivesArgsAsIntendedWithWindowsStyleArgEscaping() throws Exception { + assertSubprocessReceivesArgsAsIntended( + true, + x -> ShellUtils.windowsEscapeArg(x), + new ArgPair("", "\"\""), + new ArgPair(" ", "\" \""), + new ArgPair("\"", "\"\\\"\""), + new ArgPair("\"\\", "\"\\\"\\\\\""), + new ArgPair("\\", "\\"), + new ArgPair("\\\"", "\"\\\\\\\"\""), + new ArgPair("with space", "\"with space\""), + new ArgPair("with^caret", "with^caret"), + new ArgPair("space ^caret", "\"space ^caret\""), + new ArgPair("caret^ space", "\"caret^ space\""), + new ArgPair("with\"quote", "\"with\\\"quote\""), + new ArgPair("with\\backslash", "with\\backslash"), + new ArgPair("one\\ backslash and \\space", "\"one\\ backslash and \\space\""), + new ArgPair("two\\\\backslashes", "two\\\\backslashes"), + new ArgPair("two\\\\ backslashes \\\\and space", "\"two\\\\ backslashes \\\\and space\""), + new ArgPair("one\\\"x", "\"one\\\\\\\"x\""), + new ArgPair("two\\\\\"x", "\"two\\\\\\\\\\\"x\""), + new ArgPair("a \\ b", "\"a \\ b\""), + new ArgPair("a \\\" b", "\"a \\\\\\\" b\""), + new ArgPair("A", "A"), + new ArgPair("\"a\"", "\"\\\"a\\\"\""), + new ArgPair("B C", "\"B C\""), + new ArgPair("\"b c\"", "\"\\\"b c\\\"\""), + new ArgPair("D\"E", "\"D\\\"E\""), + new ArgPair("\"d\"e\"", "\"\\\"d\\\"e\\\"\""), + new ArgPair("C:\\F G", "\"C:\\F G\""), + new ArgPair("\"C:\\f g\"", "\"\\\"C:\\f g\\\"\""), + new ArgPair("C:\\H\"I", "\"C:\\H\\\"I\""), + new ArgPair("\"C:\\h\"i\"", "\"\\\"C:\\h\\\"i\\\"\""), + new ArgPair("C:\\J\\\"K", "\"C:\\J\\\\\\\"K\""), + new ArgPair("\"C:\\j\\\"k\"", "\"\\\"C:\\j\\\\\\\"k\\\"\""), + new ArgPair("C:\\L M ", "\"C:\\L M \""), + new ArgPair("\"C:\\l m \"", "\"\\\"C:\\l m \\\"\""), + new ArgPair("C:\\N O\\", "\"C:\\N O\\\\\""), + new ArgPair("\"C:\\n o\\\"", "\"\\\"C:\\n o\\\\\\\"\""), + new ArgPair("C:\\P Q\\ ", "\"C:\\P Q\\ \""), + new ArgPair("\"C:\\p q\\ \"", "\"\\\"C:\\p q\\ \\\"\""), + new ArgPair("C:\\R\\S\\", "C:\\R\\S\\"), + new ArgPair("C:\\R x\\S\\", "\"C:\\R x\\S\\\\\""), + new ArgPair("\"C:\\r\\s\\\"", "\"\\\"C:\\r\\s\\\\\\\"\""), + new ArgPair("\"C:\\r x\\s\\\"", "\"\\\"C:\\r x\\s\\\\\\\"\""), + new ArgPair("C:\\T U\\W\\", "\"C:\\T U\\W\\\\\""), + new ArgPair("\"C:\\t u\\w\\\"", "\"\\\"C:\\t u\\w\\\\\\\"\"")); + } }