package uk.co.nickthecoder.feather.runtime.command;
public class ShShell implements Shell {
static ShShell instance = new ShShell();
/**
* Given that a command is made up of an array of String parts, build the complete command line string.
* It is intended to be used with the unix shell "sh" and the many variants, such as bash, dash etc.
* <p>
* If the Feather script contained :
* <p>
* $( echo 'Hello $name' )
* <p>
* Then there are THREE parts e.g. : "echo 'Hello ", "Colin O'Reilly" and "'"
* The second part was from the feather variable "name", and contains a single quote.
* From the first part, we see a quote before "Hello", so subsequent non-literal parts
* must be escaped.
* So when we get to the 2nd part, we escape the quote to form :
* <p>
* Colin O'\''Reilly
* <p>
* The 3rd part is a literal, so we don't attempt to escape it, and instead count the number
* of quotes, and we find the end quote, so if there were more parts, these wouldn't be escaped.
*/
@Override
public String[] buildShell(CommandBase commandBase) {
return new String[]{"sh", "-c", buildCommandString(commandBase)};
}
protected final String buildCommandString(CommandBase commandBase) {
if (commandBase instanceof Pipe) {
Pipe pipe = (Pipe) commandBase;
return buildCommandString(pipe.producer) + " | " + buildCommandString(pipe.consumer);
}
if (commandBase instanceof Redirect) {
Redirect redirect = (Redirect) commandBase;
String result = buildCommandString(redirect.command);
if (redirect.outFile != null) {
if (redirect.appendOut) {
result += " >> " + quote(redirect.outFile.getPath());
} else {
result += " > " + quote(redirect.outFile.getPath());
}
}
if (redirect.errFile != null) {
if (redirect.errFile == redirect.outFile) {
result += " 2>&1";
} else {
if (redirect.appendErr) {
result += " 2>> " + quote(redirect.errFile.getPath());
} else {
result += " 2> " + quote(redirect.errFile.getPath());
}
}
}
if (redirect.inFile != null) {
result += " < " + quote(redirect.inFile.getPath());
}
return result;
}
if (!(commandBase instanceof Command)) {
throw new IllegalArgumentException("Expected a Command, Pipe or Redirect, but found " + commandBase.getClass().getSimpleName());
}
Command command = (Command) commandBase;
StringBuilder result = new StringBuilder();
boolean needsEscaping = false;
for (Command.CommandPart part : command.parts) {
if (part.isLiteral) {
result.append(part.str);
int indexOfQuote = part.str.indexOf('\'');
while (indexOfQuote >= 0) {
needsEscaping = !needsEscaping;
indexOfQuote = part.str.indexOf('\'', indexOfQuote + 1);
}
} else {
if (needsEscaping) {
result.append(escape(part.str));
} else {
result.append(part.str);
}
}
}
return result.toString();
}
private String quote(String str) {
return "'" + escape(str) + "'";
}
private String escape(String str) {
return str.replace("'", "'\\''");
}
}