diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIGDBSetArgs.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIGDBSetArgs.java index 620de2d873e..8152b46825b 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIGDBSetArgs.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIGDBSetArgs.java @@ -7,12 +7,13 @@ * * Contributors: * Ericsson - Initial API and implementation - * Sergey Prigogin (Google) + * Sergey Prigogin (Google) * Marc Khouzam (Ericsson) - Support empty arguments (bug 412471) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service.command.commands; +import java.util.ArrayList; import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext; /** @@ -23,25 +24,55 @@ import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext; */ public class MIGDBSetArgs extends MIGDBSet { - /** @since 4.0 */ + /** @since 4.0 */ public MIGDBSetArgs(IMIContainerDMContext dmc) { - this(dmc, new String[0]); - } + this(dmc, new String[0]); + } - /** @since 4.0 */ - public MIGDBSetArgs(IMIContainerDMContext dmc, String[] arguments) { - super(dmc, null); + /** @since 4.0 */ + public MIGDBSetArgs(IMIContainerDMContext dmc, String[] arguments) { + super(dmc, null); + fParameters = new ArrayList(); + fParameters.add(new MIStandardParameterAdjustable("args")); //$NON-NLS-1$ + for (int i = 0; i < arguments.length; i++) { + fParameters.add(new MIArgumentAdjustable(arguments[i])); + } + } - String[] cmdArray = new String[arguments.length + 1]; - cmdArray[0] = "args"; //$NON-NLS-1$ - for (int i = 0; i < arguments.length; i++) { - if (arguments[i].isEmpty()) { - // An empty parameter can be passed with two single quotes - cmdArray[i + 1] = "''"; //$NON-NLS-1$ - } else { - cmdArray[i + 1] = arguments[i]; - } - } - setParameters(cmdArray); - } + private static class MIArgumentAdjustable extends MICommandAdjustable { + + public MIArgumentAdjustable(String value) { + super(value); + } + + @Override + public String getAdjustedValue() { + // Replace and concatenate all occurrences of: + // ' with "'" + // (as ' is used to surround everything else + // it has to be quoted or escaped) + // newline character with $'\n' + // (\n is treated literally within quotes or + // as just 'n' otherwise, whilst supplying + // the newline character literally ends the command) + // Anything in between and around these occurrences + // is surrounded by single quotes. + // (to prevent bash from carrying out substitutions + // or running arbitrary code with backticks or $()) + StringBuilder builder = new StringBuilder(); + builder.append('\''); + for (int j = 0; j < value.length(); j++) { + char c = value.charAt(j); + if (c == '\'') { + builder.append("'\"'\"'"); //$NON-NLS-1$ + } else if (c == '\n') { + builder.append("'$'\\n''"); //$NON-NLS-1$ + } else { + builder.append(c); + } + } + builder.append('\''); + return builder.toString(); + } + } } diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/LaunchConfigurationAndRestartTest.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/LaunchConfigurationAndRestartTest.java index e59f3ea2794..e8ec515d434 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/LaunchConfigurationAndRestartTest.java +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/LaunchConfigurationAndRestartTest.java @@ -552,20 +552,12 @@ public class LaunchConfigurationAndRestartTest extends BaseTestCase { fExpService.getFormattedValueContext(argcDmc, MIExpressions.DETAILS_FORMAT), rm); } }; - try { - fExpService.getExecutor().execute(query); - FormattedValueDMData value = query.get(500, TimeUnit.MILLISECONDS); + fExpService.getExecutor().execute(query); + FormattedValueDMData value = query.get(500, TimeUnit.MILLISECONDS); - // Argc should be 2: the program name and the one arguments - assertTrue("Expected 2 but got " + value.getFormattedValue(), - value.getFormattedValue().trim().equals("2")); - } catch (InterruptedException e) { - fail(e.getMessage()); - } catch (ExecutionException e) { - fail(e.getCause().getMessage()); - } catch (TimeoutException e) { - fail(e.getMessage()); - } + // Argc should be 2: the program name and the one arguments + assertTrue("Expected 2 but got " + value.getFormattedValue(), + value.getFormattedValue().trim().equals("2")); // Check that argv is also correct. final IExpressionDMContext argvDmc = SyncUtil.createExpression(stoppedEvent.getDMContext(), "argv[argc-1]"); @@ -576,18 +568,64 @@ public class LaunchConfigurationAndRestartTest extends BaseTestCase { fExpService.getFormattedValueContext(argvDmc, MIExpressions.DETAILS_FORMAT), rm); } }; - try { - fExpService.getExecutor().execute(query2); - FormattedValueDMData value = query2.get(500, TimeUnit.MILLISECONDS); - assertTrue("Expected \"" + argumentUsedByGDB + "\" but got " + value.getFormattedValue(), - value.getFormattedValue().trim().endsWith(argumentUsedByGDB)); - } catch (InterruptedException e) { - fail(e.getMessage()); - } catch (ExecutionException e) { - fail(e.getCause().getMessage()); - } catch (TimeoutException e) { - fail(e.getMessage()); - } + fExpService.getExecutor().execute(query2); + value = query2.get(500, TimeUnit.MILLISECONDS); + assertTrue("Expected \"" + argumentUsedByGDB + "\" but got " + value.getFormattedValue(), + value.getFormattedValue().trim().endsWith(argumentUsedByGDB)); + } + + /** + * This test will tell the launch to set some more arguments for the program. We will + * then check that the program has the same arguments. + * See bug 474648 + */ + @Test + public void testSettingArgumentsWithSpecialSymbols() throws Throwable { + // Test that arguments are parsed correctly: + // The string provided by the user is split into arguments on spaces + // except for those inside quotation marks, double or single. + // Any character within quotation marks or after the backslash character + // is treated literally, whilst these special characters have to be + // escaped explicitly to be recorded. + // All other characters including semicolons, backticks, pipes, dollars and newlines + // must be treated literally. + String argumentToPreserveSpaces = "--abc=\"x;y;z\nsecondline: \"`date`$PS1\"`date | wc`\""; + String argumentUsedByGDB = "\"--abc=x;y;z\\nsecondline: `date`$PS1`date | wc`\""; + + setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, argumentToPreserveSpaces); + doLaunch(); + + MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); + + // Check that argc is correct + final IExpressionDMContext argcDmc = SyncUtil.createExpression(stoppedEvent.getDMContext(), "argc"); + Query query = new Query() { + @Override + protected void execute(DataRequestMonitor rm) { + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(argcDmc, MIExpressions.DETAILS_FORMAT), rm); + } + }; + fExpService.getExecutor().execute(query); + FormattedValueDMData value = query.get(500, TimeUnit.MILLISECONDS); + + // Argc should be 2: the program name and the four arguments. + assertTrue("Expected 2 but got " + value.getFormattedValue(), + value.getFormattedValue().trim().equals("2")); + + // Check that argv is also correct. + final IExpressionDMContext argvDmc = SyncUtil.createExpression(stoppedEvent.getDMContext(), "argv[argc-1]"); + Query query2 = new Query() { + @Override + protected void execute(DataRequestMonitor rm) { + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(argvDmc, MIExpressions.DETAILS_FORMAT), rm); + } + }; + fExpService.getExecutor().execute(query2); + value = query2.get(500, TimeUnit.MILLISECONDS); + assertTrue("Expected \"" + argumentUsedByGDB + "\" but got " + value.getFormattedValue(), + value.getFormattedValue().endsWith(argumentUsedByGDB)); } /**