/**
* $Id: $
* $Date: $
*
*/
package org.xmlsh.sh.core;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringReader;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import net.sf.saxon.s9api.DocumentBuilder;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.XdmNode;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.xmlsh.core.CoreException;
import org.xmlsh.core.EvalEnv;
import org.xmlsh.core.EvalFlag;
import org.xmlsh.core.XValue;
import org.xmlsh.core.XVariable;
import org.xmlsh.core.io.VariableOutputPort;
import org.xmlsh.sh.grammar.Token;
import org.xmlsh.sh.shell.ParseResult;
import org.xmlsh.sh.shell.Shell;
import org.xmlsh.types.TypeFamily;
import org.xmlsh.util.ByteFilterOutputStream;
import org.xmlsh.util.NullInputStream;
import org.xmlsh.util.Util;
/*
* A Value that evaulates to a "cmd_word" which is either a simple string,
* or a subprocess expression
*
*/
public class CommandNameWord extends Word {
String mType; // $( $(< $<( $<(< `
CommandExpr mCommand;
static Logger mLogger = LogManager.getLogger();
public CommandNameWord(Token ttype, CommandExpr c) {
super(ttype);
mType = ttype.toString();
mCommand = c;
}
@Override
public void print(PrintWriter out) {
out.print(mType);
mCommand.print(out, false);
out.print(")");
}
private String expandSubproc(Shell parentShell, CommandExpr c)
throws CoreException, IOException {
mLogger.entry(parentShell, c);
try (Shell shell = parentShell.clone();
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
OutputStream commandOut = shell.getSerializeOpts().isIgncr()
? new ByteFilterOutputStream(out, '\r') : out;
shell.getEnv().setStdout(commandOut);
shell.getEnv().setStdin(new NullInputStream());
int ret = shell.exec(c);
parentShell.setStatus(ret);
commandOut.flush();
out.flush();
String s = out
.toString(shell.getSerializeOpts().getInput_text_encoding());
// remove trailing newlines
s = Util.removeTrailingNewlines(s, shell.getSerializeOpts().isIgncr());
return mLogger.exit(s);
}
}
/*
* Parse an XValue subprocess expression like
* $<( command )
*
* Create a temporary output variable and use VariableOutputPort
* run the command to the output then extract the value from the variable
*
*/
private XValue parseXCmd(Shell parentShell, CommandExpr cmd)
throws IOException, CoreException {
mLogger.entry(parentShell, cmd);
XVariable var = XVariable.anonymousInstance(TypeFamily.XDM);
try (VariableOutputPort port = new VariableOutputPort(var)) {
try (Shell shell = parentShell.clone()) {
shell.getEnv().setStdout(port);
shell.getEnv().setStdin(new NullInputStream());
int ret = shell.exec(cmd);
parentShell.setStatus(ret);
}
XValue value = var.getValue();
port.flush();
/*
* If port was written to as a text stream then need to reparse it as a
* document
*/
if(value != null && port.isAsText()) {
String sDoc = value.toString();
Source source = new StreamSource(new StringReader(sDoc));
DocumentBuilder builder = Shell.getProcessor().newDocumentBuilder();
XdmNode node;
try {
node = builder.build(source);
} catch (SaxonApiException e) {
throw new CoreException("Exception parsing as XML Document", e);
}
return mLogger.exit(XValue.newXValue(node));
}
return mLogger.exit(value);
}
}
@Override
public boolean isEmpty() {
return mType == null || mCommand == null;
}
@Override
public String toString() {
return mType + mCommand.describe(false) + ")";
}
@Override
public String getSimpleName() {
return mCommand.getName();
}
@Override
protected ParseResult expandToResult(Shell shell, EvalEnv env,
ParseResult result) throws CoreException, IOException {
assert (result != null);
if(mType.equals("$(") || mType.equals("`")) {
// http://www.gnu.org/software/bash/manual/bashref.html#Command-Substitution
/*
* Bash performs the expansion by executing command and replacing the
* command substitution with the standard output of the command, with any
* trailing newlines deleted. Embedded newlines are not deleted, but they
* may be removed during word splitting.
* The command substitution $(cat file) can be replaced by the equivalent
* but faster $(< file).
*/
String svalue = expandSubproc(shell, mCommand);
XValue value = EvalUtils.splitStringToValue(shell, svalue, evalEnv(env));
result = EvalUtils.expandValueToResult(shell, value,
env.withFlagOff(EvalFlag.EXPAND_VAR), result);
}
else if(mType.equals("$<(")) {
XValue value = parseXCmd(shell, mCommand);
result.add(value, true);
}
else
throw new CoreException("Unexpected CommandWord: " + mCommand);
return result;
}
}
//
//
// Copyright (C) 2008-2014 David A. Lee.
//
// The contents of this file are subject to the "Simplified BSD License" (the
// "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the
// License at http://www.opensource.org/licenses/bsd-license.php
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
// See the License for the specific language governing rights and limitations
// under the License.
//
// The Original Code is: all this file.
//
// The Initial Developer of the Original Code is David A. Lee
//
// Portions created by (your name) are Copyright (C) (your legal entity). All
// Rights Reserved.
//
// Contributor(s): none.
//