Commit 9d3c3811 authored by Andrey Filippov's avatar Andrey Filippov

more editing, added console script timeout and connected Eclipse

pattern matching
parent 992d3ccf
...@@ -328,10 +328,12 @@ public class LaunchCore { ...@@ -328,10 +328,12 @@ public class LaunchCore {
launch.setAttribute(DebugPlugin.ATTR_CAPTURE_OUTPUT, null); launch.setAttribute(DebugPlugin.ATTR_CAPTURE_OUTPUT, null);
DebugPlugin.getDefault().getLaunchManager().addLaunch(launch); DebugPlugin.getDefault().getLaunchManager().addLaunch(launch);
VDTRunner runner = VDTLaunchUtil.getRunner(); VDTRunner runner = VDTLaunchUtil.getRunner();
int numItem=configuration.getBuildStep();
runner.run(configuration, runner.run(configuration,
VDTRunner.renderProcessLabel(configuration.getToolName()), // toolname + (date) VDTRunner.renderProcessLabel(configuration.getToolName()), // toolname + (date)
launch, launch,
null); null,
numItem);
} // launch() } // launch()
......
...@@ -19,6 +19,8 @@ package com.elphel.vdt.core.launching; ...@@ -19,6 +19,8 @@ package com.elphel.vdt.core.launching;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import org.eclipse.debug.internal.ui.views.console.ProcessConsole; import org.eclipse.debug.internal.ui.views.console.ProcessConsole;
import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.CoreException;
...@@ -49,6 +51,7 @@ import com.elphel.vdt.core.tools.contexts.BuildParamsItem; ...@@ -49,6 +51,7 @@ import com.elphel.vdt.core.tools.contexts.BuildParamsItem;
import com.elphel.vdt.ui.MessageUI; import com.elphel.vdt.ui.MessageUI;
import com.elphel.vdt.veditor.VerilogPlugin; import com.elphel.vdt.veditor.VerilogPlugin;
import com.elphel.vdt.veditor.preference.PreferenceStrings; import com.elphel.vdt.veditor.preference.PreferenceStrings;
import com.sun.net.ssl.internal.www.protocol.https.Handler;
public class VDTConsoleRunner{ public class VDTConsoleRunner{
private final VDTRunnerConfiguration runConfig; private final VDTRunnerConfiguration runConfig;
...@@ -67,7 +70,8 @@ public class VDTConsoleRunner{ ...@@ -67,7 +70,8 @@ public class VDTConsoleRunner{
public VDTConsoleRunner (VDTRunnerConfiguration runConfig){ public VDTConsoleRunner (VDTRunnerConfiguration runConfig){
this.runConfig=runConfig; this.runConfig=runConfig;
} }
private BuildParamsItem getParser( String parserName){ /*
* private BuildParamsItem getParser( String parserName){
if (parserName==null) return null; if (parserName==null) return null;
BuildParamsItem[] buildParamsItems = runConfig.getArgumentsItemsArray(); // uses already calculated BuildParamsItem[] buildParamsItems = runConfig.getArgumentsItemsArray(); // uses already calculated
if (buildParamsItems==null) return null; if (buildParamsItems==null) return null;
...@@ -78,6 +82,19 @@ public class VDTConsoleRunner{ ...@@ -78,6 +82,19 @@ public class VDTConsoleRunner{
return null; return null;
} }
*/
private int getParserIndex( String parserName){
if (parserName==null) return -1;
BuildParamsItem[] buildParamsItems = runConfig.getArgumentsItemsArray(); // uses already calculated
if (buildParamsItems==null) return -1;
for (int i=0;i<buildParamsItems.length;i++){
if (parserName.equals(buildParamsItems[i].getNameAsParser()))
return i;
}
return -1;
}
public IOConsole runConsole(String consolePrefix public IOConsole runConsole(String consolePrefix
, ILaunch launch , ILaunch launch
, IProgressMonitor monitor , IProgressMonitor monitor
...@@ -124,9 +141,12 @@ public class VDTConsoleRunner{ ...@@ -124,9 +141,12 @@ public class VDTConsoleRunner{
process=((ProcessConsole)iCons).getProcess(); process=((ProcessConsole)iCons).getProcess();
// IStreamsProxy consoleInStreamProxy= process.getStreamsProxy(); // IStreamsProxy consoleInStreamProxy= process.getStreamsProxy();
consoleInStreamProxy= process.getStreamsProxy(); consoleInStreamProxy= process.getStreamsProxy();
int stderrParserIndex=getParserIndex(buildParamsItem.getStderr());
BuildParamsItem stderrParser=getParser(buildParamsItem.getStderr()); // re-parses all - why? int stdoutParserIndex=getParserIndex(buildParamsItem.getStdout());
BuildParamsItem stdoutParser=getParser(buildParamsItem.getStdout()); BuildParamsItem stderrParser=(stderrParserIndex>=0)?runConfig.getArgumentsItemsArray()[stderrParserIndex]:null;
BuildParamsItem stdoutParser=(stdoutParserIndex>=0)?runConfig.getArgumentsItemsArray()[stdoutParserIndex]:null;
// BuildParamsItem stderrParser=getParser(buildParamsItem.getStderr()); // re-parses all - why?
// BuildParamsItem stdoutParser=getParser(buildParamsItem.getStdout());
System.out.println("Using parser for stderr: "+((stderrParser!=null)?stderrParser.getNameAsParser():"none")); // actually may be the same as stdout System.out.println("Using parser for stderr: "+((stderrParser!=null)?stderrParser.getNameAsParser():"none")); // actually may be the same as stdout
System.out.println("Using parser for stdout: "+((stdoutParser!=null)?stdoutParser.getNameAsParser():"none")); System.out.println("Using parser for stdout: "+((stdoutParser!=null)?stdoutParser.getNameAsParser():"none"));
...@@ -145,7 +165,8 @@ public class VDTConsoleRunner{ ...@@ -145,7 +165,8 @@ public class VDTConsoleRunner{
processOut=runner.run(runConfig, processOut=runner.run(runConfig,
"OUT for "+iCons.getName(), "OUT for "+iCons.getName(),
launch, launch,
null); //monitor); null, //monitor
stdoutParserIndex);
stdoutStreamProxy= processOut.getStreamsProxy(); stdoutStreamProxy= processOut.getStreamsProxy();
//TODO: Add error parsers //TODO: Add error parsers
} }
...@@ -160,7 +181,8 @@ public class VDTConsoleRunner{ ...@@ -160,7 +181,8 @@ public class VDTConsoleRunner{
processErr=runner.run(runConfig, processErr=runner.run(runConfig,
"ERR for "+iCons.getName(), "ERR for "+iCons.getName(),
launch, launch,
null); //monitor); null, //monitor);
stderrParserIndex);
stderrStreamProxy= processErr.getStreamsProxy(); stderrStreamProxy= processErr.getStreamsProxy();
//TODO: Add error parsers //TODO: Add error parsers
} }
...@@ -229,8 +251,20 @@ public class VDTConsoleRunner{ ...@@ -229,8 +251,20 @@ public class VDTConsoleRunner{
} catch (IOException e) { } catch (IOException e) {
System.out.println("Can not write to outStream of console "+iCons.getName()); System.out.println("Can not write to outStream of console "+iCons.getName());
} }
int timeout=buildParamsItem.getTimeout();
if ((timeout==0) && (buildParamsItem.getPrompt()==null)) timeout=1;// should specify at least one of timeout or prompt
if (timeout>0){
System.out.println("Setting timeout "+timeout);
final int fTimeout = timeout;
new Timer().schedule(new TimerTask() {
@Override
public void run() {
System.out.println(">>Timeout<<");
finishConsolescript();
}
}, fTimeout*1000);
}
return iCons; return iCons;
} }
......
...@@ -26,6 +26,8 @@ import java.util.Date; ...@@ -26,6 +26,8 @@ import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import org.eclipse.debug.internal.ui.views.console.ProcessConsole; import org.eclipse.debug.internal.ui.views.console.ProcessConsole;
//import org.eclipse.core.resources.IProject; //import org.eclipse.core.resources.IProject;
...@@ -36,6 +38,7 @@ import org.eclipse.core.runtime.IStatus; ...@@ -36,6 +38,7 @@ import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfiguration;
...@@ -78,6 +81,8 @@ import com.elphel.vdt.veditor.preference.PreferenceStrings; ...@@ -78,6 +81,8 @@ import com.elphel.vdt.veditor.preference.PreferenceStrings;
//import com.elphel.vdt.core.Utils; //import com.elphel.vdt.core.Utils;
import org.eclipse.ui.console.IConsoleListener; import org.eclipse.ui.console.IConsoleListener;
...@@ -137,7 +142,9 @@ public class VDTRunner { ...@@ -137,7 +142,9 @@ public class VDTRunner {
// renderProcessLabel(runConfig.getToolName()), // toolname + (date) // renderProcessLabel(runConfig.getToolName()), // toolname + (date)
runConfig.getOriginalConsoleName(), runConfig.getOriginalConsoleName(),
launch, launch,
monitor); monitor,
numItem
);
//Andrey: if there is a single item - launch asynchronously, if more - verify queue is empty //Andrey: if there is a single item - launch asynchronously, if more - verify queue is empty
// will not change // will not change
...@@ -148,6 +155,8 @@ public class VDTRunner { ...@@ -148,6 +155,8 @@ public class VDTRunner {
runningBuilds.removeConfiguration(consoleName); runningBuilds.removeConfiguration(consoleName);
return; return;
} }
if (numItem<(argumentsItemsArray.length-1)){ // Not for the last if (numItem<(argumentsItemsArray.length-1)){ // Not for the last
// IConsoleManager man = ConsolePlugin.getDefault().getConsoleManager(); // debugging // IConsoleManager man = ConsolePlugin.getDefault().getConsoleManager(); // debugging
// IConsole[] consoles=(IConsole[]) man.getConsoles(); // IConsole[] consoles=(IConsole[]) man.getConsoles();
...@@ -191,6 +200,23 @@ public class VDTRunner { ...@@ -191,6 +200,23 @@ public class VDTRunner {
iCons.firePropertyChange(fiCons,"org.eclipse.jface.text", consoleName, fConsoleName); iCons.firePropertyChange(fiCons,"org.eclipse.jface.text", consoleName, fConsoleName);
} }
System.out.println("return - waiting to be awaken"); System.out.println("return - waiting to be awaken");
int timeout=argumentsItemsArray[numItem].getTimeout();
if (timeout>0){
final int fTimeout = timeout;
final IProcess fProcess=process;
new Timer().schedule(new TimerTask() {
@Override
public void run() {
System.out.println(">>Timeout<<");
try {
fProcess.terminate();
} catch (DebugException e) {
System.out.println("Failed to terminate preocess on "+fConsoleName);
}
}
}, fTimeout*1000);
}
return; return;
} }
...@@ -257,13 +283,14 @@ public class VDTRunner { ...@@ -257,13 +283,14 @@ public class VDTRunner {
, String consoleLabel , String consoleLabel
, ILaunch launch , ILaunch launch
, IProgressMonitor monitor , IProgressMonitor monitor
, int numItem
) throws CoreException ) throws CoreException
{ {
if (monitor == null) { if (monitor == null) {
monitor = new NullProgressMonitor(); monitor = new NullProgressMonitor();
} }
int numItem=configuration.getBuildStep(); // int numItem=configuration.getBuildStep();
// BuildParamsItem buildParamsItem= VDTLaunchUtil.getArguments(configuration.getConfiguration())[numItem];
BuildParamsItem buildParamsItem = configuration.getArgumentsItemsArray()[numItem]; // uses already calculated BuildParamsItem buildParamsItem = configuration.getArgumentsItemsArray()[numItem]; // uses already calculated
String patternErrors= combinePatterns(buildParamsItem.getErrors(), configuration.getPatternErrors()) ; String patternErrors= combinePatterns(buildParamsItem.getErrors(), configuration.getPatternErrors()) ;
String patternWarnings=combinePatterns(buildParamsItem.getWarnings(),configuration.getPatternWarnings()) ; String patternWarnings=combinePatterns(buildParamsItem.getWarnings(),configuration.getPatternWarnings()) ;
......
...@@ -67,6 +67,7 @@ public class CommandLinesNodeReader extends AbstractConditionNodeReader { ...@@ -67,6 +67,7 @@ public class CommandLinesNodeReader extends AbstractConditionNodeReader {
String stderr = XMLConfig.getAttributeValue(node, XMLConfig.CONTEXT_LINEBLOCK_STDERR_ATTR); String stderr = XMLConfig.getAttributeValue(node, XMLConfig.CONTEXT_LINEBLOCK_STDERR_ATTR);
String stdout = XMLConfig.getAttributeValue(node, XMLConfig.CONTEXT_LINEBLOCK_STDOUT_ATTR); String stdout = XMLConfig.getAttributeValue(node, XMLConfig.CONTEXT_LINEBLOCK_STDOUT_ATTR);
String timeout = XMLConfig.getAttributeValue(node, XMLConfig.CONTEXT_LINEBLOCK_TIMEOUT_ATTR);
if(name == null) if(name == null)
throw new ConfigException("Unnamed lines block definition in context '" + throw new ConfigException("Unnamed lines block definition in context '" +
...@@ -92,6 +93,7 @@ public class CommandLinesNodeReader extends AbstractConditionNodeReader { ...@@ -92,6 +93,7 @@ public class CommandLinesNodeReader extends AbstractConditionNodeReader {
interrupt, interrupt,
stderr, stderr,
stdout, stdout,
timeout,
lines, lines,
deleteLines, deleteLines,
insertLines); insertLines);
......
...@@ -135,6 +135,7 @@ public class XMLConfig extends Config { ...@@ -135,6 +135,7 @@ public class XMLConfig extends Config {
static final String CONTEXT_LINEBLOCK_INTERRUPT_ATTR ="interrupt"; static final String CONTEXT_LINEBLOCK_INTERRUPT_ATTR ="interrupt";
static final String CONTEXT_LINEBLOCK_STDERR_ATTR = "stderr"; static final String CONTEXT_LINEBLOCK_STDERR_ATTR = "stderr";
static final String CONTEXT_LINEBLOCK_STDOUT_ATTR = "stdout"; static final String CONTEXT_LINEBLOCK_STDOUT_ATTR = "stdout";
static final String CONTEXT_LINEBLOCK_TIMEOUT_ATTR = "timeout";
static final String CONTEXT_STRINGS_DELETE_TAG = "delete"; static final String CONTEXT_STRINGS_DELETE_TAG = "delete";
static final String CONTEXT_STRINGS_INSERT_TAG = "insert"; static final String CONTEXT_STRINGS_INSERT_TAG = "insert";
......
...@@ -36,7 +36,7 @@ public class BuildParamsItem implements Cloneable{ ...@@ -36,7 +36,7 @@ public class BuildParamsItem implements Cloneable{
private String stdout; // name of the command to (command line block) to launch in a separate process/console private String stdout; // name of the command to (command line block) to launch in a separate process/console
// and connect to stderr of the terminal session // and connect to stderr of the terminal session
private int timeout; // timeout for console tasks, in seconds
public BuildParamsItem ( public BuildParamsItem (
String [] params, String [] params,
...@@ -49,7 +49,8 @@ public class BuildParamsItem implements Cloneable{ ...@@ -49,7 +49,8 @@ public class BuildParamsItem implements Cloneable{
String prompt, String prompt,
String interrupt, String interrupt,
String stderr, String stderr,
String stdout String stdout,
int timeout
) { ) {
this.consoleName=consoleName; this.consoleName=consoleName;
this.params=params; // no need to clone? this.params=params; // no need to clone?
...@@ -62,6 +63,7 @@ public class BuildParamsItem implements Cloneable{ ...@@ -62,6 +63,7 @@ public class BuildParamsItem implements Cloneable{
this.interrupt=interrupt; this.interrupt=interrupt;
this.stderr=stderr; this.stderr=stderr;
this.stdout=stdout; this.stdout=stdout;
this.timeout=timeout;
} }
public BuildParamsItem (BuildParamsItem item){ public BuildParamsItem (BuildParamsItem item){
...@@ -76,7 +78,8 @@ public class BuildParamsItem implements Cloneable{ ...@@ -76,7 +78,8 @@ public class BuildParamsItem implements Cloneable{
item.prompt, item.prompt,
item.interrupt, item.interrupt,
item.stderr, item.stderr,
item.stdout item.stdout,
item.timeout
); );
} }
...@@ -135,4 +138,5 @@ public class BuildParamsItem implements Cloneable{ ...@@ -135,4 +138,5 @@ public class BuildParamsItem implements Cloneable{
public String getInterrupt() { return interrupt; } public String getInterrupt() { return interrupt; }
public String getStderr() { return stderr; } public String getStderr() { return stderr; }
public String getStdout() { return stdout; } public String getStdout() { return stdout; }
public int getTimeout() { return timeout; }
} }
...@@ -216,6 +216,12 @@ public abstract class Context { ...@@ -216,6 +216,12 @@ public abstract class Context {
String stderr=commandLinesBlock.getStderr(); String stderr=commandLinesBlock.getStderr();
String stdout=commandLinesBlock.getStdout(); String stdout=commandLinesBlock.getStdout();
String prompt=buildSimpleString(commandLinesBlock.getPrompt()); // evaluate string String prompt=buildSimpleString(commandLinesBlock.getPrompt()); // evaluate string
String sTimeout=buildSimpleString(commandLinesBlock.getTimeout());
int timeout=0;
try{
timeout=Integer.parseInt(sTimeout);
} catch(Exception e){
}
prompt=commandLinesBlock.parseCntrl(prompt); // replace control character codes (\n,\t,\x) prompt=commandLinesBlock.parseCntrl(prompt); // replace control character codes (\n,\t,\x)
prompt=commandLinesBlock.applyMark(prompt); // remove mark sequence prompt=commandLinesBlock.applyMark(prompt); // remove mark sequence
String interrupt=commandLinesBlock.getInterrupt(); String interrupt=commandLinesBlock.getInterrupt();
...@@ -247,7 +253,9 @@ public abstract class Context { ...@@ -247,7 +253,9 @@ public abstract class Context {
prompt, prompt,
interrupt, interrupt,
stderr, stderr,
stdout) stdout,
timeout
)
); );
} else { // processing command file } else { // processing command file
if(workingDirectory != null) if(workingDirectory != null)
...@@ -282,7 +290,9 @@ public abstract class Context { ...@@ -282,7 +290,9 @@ public abstract class Context {
prompt, prompt,
interrupt, interrupt,
stderr, stderr,
stdout) stdout,
timeout
)
); );
} }
...@@ -310,13 +320,18 @@ public abstract class Context { ...@@ -310,13 +320,18 @@ public abstract class Context {
return processor.process(paramStringTemplate); return processor.process(paramStringTemplate);
} }
// recognizes parameter name (just %name), or simple generators
protected String buildSimpleString(String stringTemplate) protected String buildSimpleString(String stringTemplate)
throws ToolException throws ToolException
{ {
if (stringTemplate==null) return null; if (stringTemplate==null) return null;
Parameter parName=findParam(stringTemplate);
if (parName!=null){
return parName.getValue().get(0).trim(); // get parameter
}
FormatProcessor processor = new FormatProcessor(new Recognizer[] { FormatProcessor processor = new FormatProcessor(new Recognizer[] {
new SimpleGeneratorRecognizer(), new SimpleGeneratorRecognizer(),
// new RepeaterRecognizer() new RepeaterRecognizer()
// new ContextParamRecognizer(this), // new ContextParamRecognizer(this),
// new ContextParamRepeaterRecognizer(this) // new ContextParamRepeaterRecognizer(this)
}); });
...@@ -326,8 +341,6 @@ public abstract class Context { ...@@ -326,8 +341,6 @@ public abstract class Context {
return result.get(0); return result.get(0);
} }
protected void initControlInterface() throws ConfigException { protected void initControlInterface() throws ConfigException {
if(controlInterfaceName != null) { if(controlInterfaceName != null) {
controlInterface = config.findControlInterface(controlInterfaceName); controlInterface = config.findControlInterface(controlInterfaceName);
......
...@@ -50,6 +50,7 @@ public class CommandLinesBlock extends UpdateableStringsContainer ...@@ -50,6 +50,7 @@ public class CommandLinesBlock extends UpdateableStringsContainer
// If both are specified and pointing to the same command block - two instances/consoles will be launched. // If both are specified and pointing to the same command block - two instances/consoles will be launched.
// if only stdout - both stdout and stdin of a session will go to the same process/console // if only stdout - both stdout and stdin of a session will go to the same process/console
private String interrupt; // send this to remote terminal to interrupt execution (parses use \xNN) private String interrupt; // send this to remote terminal to interrupt execution (parses use \xNN)
private String timeout; // timeout for console tasks, in seconds
public CommandLinesBlock(String contextName, public CommandLinesBlock(String contextName,
...@@ -65,6 +66,7 @@ public class CommandLinesBlock extends UpdateableStringsContainer ...@@ -65,6 +66,7 @@ public class CommandLinesBlock extends UpdateableStringsContainer
String interrupt, String interrupt,
String stderr, String stderr,
String stdout, String stdout,
String timeout,
ConditionalStringsList lines, ConditionalStringsList lines,
ConditionalStringsList deleteLines, ConditionalStringsList deleteLines,
List<NamedConditionalStringsList> insertLines) List<NamedConditionalStringsList> insertLines)
...@@ -84,6 +86,7 @@ public class CommandLinesBlock extends UpdateableStringsContainer ...@@ -84,6 +86,7 @@ public class CommandLinesBlock extends UpdateableStringsContainer
this.interrupt=interrupt; this.interrupt=interrupt;
this.stderr=stderr; this.stderr=stderr;
this.stdout=stdout; this.stdout=stdout;
this.timeout=timeout;
if(this.separator != null) { if(this.separator != null) {
// separator = separator.replace("\\n", "\n"); // separator = separator.replace("\\n", "\n");
...@@ -136,6 +139,7 @@ public class CommandLinesBlock extends UpdateableStringsContainer ...@@ -136,6 +139,7 @@ public class CommandLinesBlock extends UpdateableStringsContainer
block.interrupt, block.interrupt,
block.stderr, block.stderr,
block.stdout, block.stdout,
block.timeout,
block.strings != null? block.strings != null?
(ConditionalStringsList)block.strings.clone() : null, (ConditionalStringsList)block.strings.clone() : null,
block.deleteStrings != null? block.deleteStrings != null?
...@@ -205,6 +209,7 @@ public class CommandLinesBlock extends UpdateableStringsContainer ...@@ -205,6 +209,7 @@ public class CommandLinesBlock extends UpdateableStringsContainer
public String getInterrupt() { return prompt; } public String getInterrupt() { return prompt; }
public String getStderr() { return stderr; } public String getStderr() { return stderr; }
public String getStdout() { return stdout; } public String getStdout() { return stdout; }
public String getTimeout() { return timeout; }
public boolean isEnabled() { public boolean isEnabled() {
......
...@@ -91,9 +91,15 @@ ...@@ -91,9 +91,15 @@
<parameter id="python_console_name" default="RemotePython" <parameter id="python_console_name" default="RemotePython"
type="String" format="CopyValue" visible="false" /> type="String" format="CopyValue" visible="false" />
<parameter id="Timeout" label="Script timeout(sec)" type="Cardinal"
format="CopyValue" default="10" readonly="false" visible="true" />
<input> <input>
<group name="General"> <group name="General">
"RemoteCommand" "RemoteCommand"
"Timeout"
</group> </group>
</input> </input>
...@@ -111,7 +117,8 @@ ...@@ -111,7 +117,8 @@
mark="``" mark="``"
sep="\n" sep="\n"
prompt="@@FINISH@@" prompt="@@FINISH@@"
stdout="parser_001"> stdout="parser_001"
timeout="Timeout">
"%RemoteCommand" "%RemoteCommand"
"print '@@FINISH@@'" "print '@@FINISH@@'"
"``"`" <!-- two new lines should generate a pair of prompts from the remote --> "``"`" <!-- two new lines should generate a pair of prompts from the remote -->
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment