/**
* f * $Id$
* $Date$
*
*/
package org.xmlsh.sh.shell;
import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.Stack;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import javax.xml.transform.TransformerException;
import org.apache.commons.io.output.StringBuilderWriter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.ThreadContext;
import org.xmlsh.core.CoreException;
import org.xmlsh.core.EvalEnv;
import org.xmlsh.core.ExitOnErrorException;
import org.xmlsh.core.ICommand;
import org.xmlsh.core.InputPort;
import org.xmlsh.core.InvalidArgumentException;
import org.xmlsh.core.Options;
import org.xmlsh.core.Options.OptionValue;
import org.xmlsh.core.ScriptCommand.SourceMode;
import org.xmlsh.core.SearchPath;
import org.xmlsh.core.ThrowException;
import org.xmlsh.core.UnexpectedException;
import org.xmlsh.core.Variables;
import org.xmlsh.core.XClassLoader;
import org.xmlsh.core.XDynamicVariable;
import org.xmlsh.core.XEnvironment;
import org.xmlsh.core.XValue;
import org.xmlsh.core.XVariable;
import org.xmlsh.core.XVariable.XVarFlag;
import org.xmlsh.core.io.FileInputPort;
import org.xmlsh.core.io.FileOutputPort;
import org.xmlsh.core.io.IShellPrompt;
import org.xmlsh.core.io.OutputPort;
import org.xmlsh.core.io.ShellIO;
import org.xmlsh.core.io.ShellReader;
import org.xmlsh.core.io.StreamInputPort;
import org.xmlsh.core.io.StreamOutputPort;
import org.xmlsh.sh.core.CommandExpr;
import org.xmlsh.sh.core.EvalUtils;
import org.xmlsh.sh.core.ICommandExpr;
import org.xmlsh.sh.core.IExpression;
import org.xmlsh.sh.core.SourceLocation;
import org.xmlsh.sh.core.SourceLocator;
import org.xmlsh.sh.grammar.ParseException;
import org.xmlsh.sh.grammar.ShellParser;
import org.xmlsh.sh.grammar.ShellParserReader;
import org.xmlsh.sh.module.CommandFactory;
import org.xmlsh.sh.module.IModule;
import org.xmlsh.sh.module.ModuleConfig;
import org.xmlsh.sh.module.ModuleFactory;
import org.xmlsh.sh.module.PName;
import org.xmlsh.sh.module.RootModule;
import org.xmlsh.util.FileUtils;
import org.xmlsh.util.NullInputStream;
import org.xmlsh.util.NullOutputStream;
import org.xmlsh.util.PathMatchOptions;
import org.xmlsh.util.SessionEnvironment;
import org.xmlsh.util.Util;
import org.xmlsh.xpath.EvalDefinition;
import org.xmlsh.xpath.ThreadLocalShell;
import net.sf.saxon.Configuration;
import net.sf.saxon.s9api.Processor;
public class Shell implements AutoCloseable, Closeable, IShellPrompt,
net.sf.saxon.lib.Initializer {
private static volatile int __id = 0;
private final int _id = ++__id;
private static String[] _reservedEnvVars = {
ShellConstants.PATH,
ShellConstants.PS1,
ShellConstants.PS2,
ShellConstants.ENV_PWD,
ShellConstants.VAR_RANDOM,
ShellConstants.VAR_RANDOM32,
ShellConstants.VAR_RANDOM64,
ShellConstants.ENV_XMODPATH,
ShellConstants.ENV_XMLSH_HOME,
ShellConstants.ENV_XMLSH,
ShellConstants.ENV_XPATH
};
@Override
public String toString() {
return "[" + _id + "," + mLastThreadId + "]";
}
public boolean isInFunction() {
return mCallStack != null && !mCallStack.isEmpty();
}
// The return of a function and/or command
// A interger 'exit value' and a XValue - may or may not be the same
public static class ReturnValue {
public ReturnValue(int exitStatus, XValue returnValue) {
super();
mExitStatus = exitStatus;
mReturnValue = returnValue;
}
public int mExitStatus;
public XValue mReturnValue;
}
class CallStackEntry {
public String name;
public IExpression cmd;
public SourceLocation loc;
public CallStackEntry(String name, IExpression cmd, SourceLocation loc) {
getLogger().entry(name, cmd, loc);
this.name = name;
this.cmd = cmd;
this.loc = loc;
}
public SourceLocation makeLocation() {
SourceLocation s = new SourceLocation(name,
(loc == null ? getLocation() : loc));
return s;
}
}
private static Logger _mLogger = null;
static private Logger getLogger() {
if(_mLogger == null)
_mLogger = LogManager.getLogger();
return _mLogger;
}
private ShellOpts mOpts;
private ShellIO mIO; // Command and event IO environment
private XEnvironment mEnv = null;
private List<XValue> mArgs = new CopyOnWriteArrayList<XValue>();
private String mArg0 = "xmlsh";
private SessionEnvironment mSession = null;
// Set to non null until exit or EOF
private Integer mExitVal = null;
private XValue mReturnVal = null;
private int mStatus = 0; // $? variable
private String mSavedCD = null;
private volatile List<ShellThread> mChildren = null;
private Map<String, String> mTraps = null;
private boolean mIsInteractive = false;
private long mLastThreadId = 0;
private Stack<ControlLoop> mControlStack = null;
private Stack<CallStackEntry> mCallStack = null;
// Depth of conditions used for 'throw on error'
private int mConditionDepth = 0;
// Current classloader
private SourceLocation mCurrentLocation = null;
/*
* Initializtion statics
*/
static boolean bInitialized = false;
static Properties mSavedSystemProperties;
private Processor mProcessor = null;
private Shell mParent = null;
private IFS mIFS;
private ThreadGroup mThreadGroup = null;
private volatile boolean mClosed = true;
private final static EvalEnv mPSEnv = EvalEnv.newInstance(false, false,
true, false);
private AtomicInteger mRunDepth = new AtomicInteger(); // count of depth if
// the shell is
// executing
private volatile List<Process> mChildProcess = null;
// Special flag/marker indicating a return
private final XValue mReturnFlag = XValue.nullValue();
public static void uninitialize() {
if(!bInitialized)
return;
System.setProperties(mSavedSystemProperties);
mSavedSystemProperties = null;
SystemEnvironment.uninitialize();
ThreadLocalShell.set(null);
bInitialized = false;
}
static {
// Make this happen first ?
Logger fake = _mLogger;
assert (fake == null);
ShellConstants.initialize();
}
/*
* New top level shell
*/
public Shell() throws IOException, CoreException {
this(new ShellIO(ShellReader.newNullReader()));
}
/*
* Create a new Shell.
*
*/
public Shell(ShellIO io) throws IOException, CoreException {
getLogger().entry(io);
mIO = io;
mIO.setPrompt(this);
mClosed = false;
mOpts = new ShellOpts();
mSavedCD = System.getProperty(ShellConstants.PROP_USER_DIR);
mEnv = new XEnvironment(this, new StaticContext(),
RootModule.getInstance(), io);
mSession = new SessionEnvironment();
setGlobalVars();
ThreadLocalShell.set(this); // cur thread active shell
// create processor before subshells are created
getProcessor();
try {
importStandardModules();
} catch (ClassNotFoundException | InstantiationException
| IllegalAccessException | IllegalArgumentException
| InvocationTargetException | CoreException e) {
Util.wrapCoreException("Exception importing standard modules", e);
}
}
/*
* Populate the environment with any global variables
*/
private void importStandardModules() throws ClassNotFoundException,
InstantiationException, IllegalAccessException, IllegalArgumentException,
InvocationTargetException, CoreException, IOException {
IModule hInternal = ModuleFactory.createPackageModule(this,
ModuleFactory.getInternalModuleConfig(this,
"xmlsh", Arrays.asList(
"org.xmlsh.internal.commands", "org.xmlsh.internal.functions"),
CommandFactory.kCOMMANDS_HELP_XML)
);
getModules().importModule(this, null, hInternal, null);
}
private void setGlobalVars() throws InvalidArgumentException {
// System env first
Map<String, String> env = new HashMap<>();
env.putAll(System.getenv());
Properties props = System.getProperties();
props.stringPropertyNames().stream()
.filter(s -> s.startsWith(ShellConstants.kXMLSH_PROP_PREFIX))
.forEach(key -> env.putIfAbsent(
key.substring(ShellConstants.kXMLSH_PROP_PREFIX.length()),
props.getProperty(key)));
env.keySet().stream().filter(name -> (!Util.isBlank(name) &&
!Util.isPath(name) &&
name.matches("^[a-zA-Z_0-9]+$") &&
// Ignore reserved vars that are set internally
!Util.contains(_reservedEnvVars, name)))
.forEach(name -> getEnv().initVariable(
XVariable.newInstance(name,
XValue.newXValue(env.get(name)),
XVariable.systemFlags())));
// Builtins may come from env or properties
// Export path to shell path
String path = FileUtils.toJavaPath(getSystemProperty(ShellConstants.PATH));
getEnv().initVariable(
XVariable.newInstance(
ShellConstants.PATH,
Util.isBlank(path) ? XValue.empytSequence() : XValue
.newXValue(path.split(File.pathSeparator)),
XVariable.systemFlags()));
String xpath = FileUtils
.toJavaPath(getSystemProperty(ShellConstants.ENV_XPATH));
getEnv().initVariable(
XVariable.newInstance(
ShellConstants.ENV_XPATH,
Util.isBlank(xpath) ? XValue.newXValue(".") : XValue
.newXValue(xpath.split(File.pathSeparator)),
XVariable.systemFlags()));
String xmlsh = getSystemProperty(ShellConstants.ENV_XMLSH);
String xmlshhome = getSystemProperty(ShellConstants.ENV_XMLSH_HOME);
if(Util.isBlank(xmlshhome))
xmlshhome = xmlsh;
if(Util.isBlank(xmlshhome))
xmlshhome = tryFindHome();
if(Util.isBlank(xmlsh))
xmlsh = xmlshhome;
if(Util.isBlank(xmlshhome)) {
getLogger().warn("Required property {} missing - limited functionalty.",
ShellConstants.ENV_XMLSH_HOME);
}
getEnv().setVar(
ShellConstants.ENV_XMLSH, XValue.newXValue(FileUtils.toJavaPath(xmlsh)),
XVariable.standardFlags());
getEnv().setVar(
ShellConstants.ENV_XMLSH_HOME,
XValue.newXValue(FileUtils.toJavaPath(xmlshhome)),
XVariable.standardFlags());
String xmpath = FileUtils
.toJavaPath(getSystemProperty(ShellConstants.ENV_XMODPATH));
getEnv().setVar(
ShellConstants.ENV_XMODPATH,
Util.isBlank(xmpath) ? XValue.empytSequence()
: XValue.newXValue(xmpath.split(File.pathSeparator)),
XVariable.standardFlags());
// PWD
getEnv().initVariable(
new XDynamicVariable(ShellConstants.ENV_PWD, EnumSet.of(
XVarFlag.READONLY, XVarFlag.EXPORT)) {
@Override
public XValue getValue() {
return XValue.newXValue(FileUtils.toJavaPath(getEnv()
.getCurdir().getAbsolutePath()));
}
});
// RANDOM
getEnv().initVariable(
new XDynamicVariable(ShellConstants.VAR_RANDOM, EnumSet
.of(XVarFlag.READONLY)) {
Random mRand = new Random();
@Override
public XValue getValue() {
return XValue.newXValue(mRand.nextInt(0x7FFF));
}
});
// RANDOM32
getEnv().initVariable(
new XDynamicVariable(ShellConstants.VAR_RANDOM32, EnumSet
.of(XVarFlag.READONLY)) {
Random mRand = new Random();
@Override
public XValue getValue() {
long v = mRand.nextInt();
v &= 0x7FFFFFFFL;
return XValue.newXValue((int) v);
}
});
// RANDOM
getEnv().initVariable(
new XDynamicVariable(ShellConstants.VAR_RANDOM64, EnumSet
.of(XVarFlag.READONLY)) {
Random mRand = new Random();
@Override
public XValue getValue() {
return XValue.newXValue(mRand.nextLong() & 0x7FFFFFFFFFFFFFFFL);
}
});
getEnv().setVar(
ShellConstants.ENV_TMPDIR,
XValue.newXValue(FileUtils.toJavaPath(System
.getProperty(ShellConstants.PROP_JAVA_IO_TMPDIR))),
XVariable.systemFlags());
if(getEnv().getVar(ShellConstants.ENV_HOME) == null)
getEnv().setVar(
ShellConstants.ENV_HOME,
XValue.newXValue(FileUtils.toJavaPath(System
.getProperty(ShellConstants.PROP_USER_HOME))),
XVariable.systemFlags());
}
// Try to find the install root
// http://stackoverflow.com/questions/320542/how-to-get-the-path-of-a-running-jar-file
private static String tryFindHome() {
getLogger().entry();
try {
CodeSource cs = Shell.class.getProtectionDomain().getCodeSource();
if(cs != null) {
Path p = Paths.get(cs.getLocation().toURI());
String sdir = null;
while((p = FileUtils.resolveLink(p)) != null) {
while(p != null && !Files.isDirectory(p))
p = FileUtils.getParent(p);
if(p == null)
break;
if(sdir == null)
sdir = p.toString(); // first dir found
Path parent = FileUtils.getParent(p);
if(parent == null || parent == p || Files.isSameFile(parent, p))
break;
p = parent;
if(p != null) {
if(Files.isDirectory(p.resolve("modules")) ||
Files.isDirectory(p.resolve("lib")))
return getLogger().exit(p.toString());
}
}
return getLogger().exit(sdir);
}
} catch (SecurityException | URISyntaxException | IOException e) {
getLogger().catching(e);
}
return null;
}
static String getSystemProperty(String prop) {
String propValue = System.getenv(prop);
if(propValue == null)
propValue = System.getProperty(ShellConstants.kXMLSH_PROP_PREFIX + prop);
return propValue;
}
/*
* Cloned shell for sub-thread execution
*/
private Shell(Shell that) throws IOException {
this(that, that.getThreadGroup());
}
private Shell(Shell that, ThreadGroup threadGroup) throws IOException {
getLogger().entry(that, threadGroup);
mIO = that.mIO;
mClosed = false;
mParent = that;
mProcessor = that.mProcessor;
mThreadGroup = threadGroup == null ? that.getThreadGroup()
: threadGroup;
mOpts = new ShellOpts(that.mOpts);
mEnv = that.getEnv().clone(this);
mArg0 = that.mArg0;
// clone $1..$N
mArgs = new CopyOnWriteArrayList<XValue>(that.mArgs);
mSavedCD = System.getProperty(ShellConstants.PROP_USER_DIR);
// mModule.addRef? mModule.clone() ?
// Pass through the Session Enviornment, keep a reference
mSession = that.mSession;
mSession.addRef();
// Cloning shells doesnt save the condition depth
// mConditionDepth = that.mConditionDepth;
getLogger().exit();
}
@Override
public Shell clone() {
getLogger().entry();
try {
return new Shell(this);
} catch (IOException e) {
printErr("Exception cloning shell", e);
return null;
}
}
@Override
public void close() throws IOException {
getLogger().entry();
// Synchronized only to change states
synchronized(this) {
getLogger().trace("closing {} - closed is: {} ", this, mClosed);
if(mClosed) {
getLogger().exit();
return;
}
// Mark closed now while syncronized
mClosed = true;
}
if(mParent != null)
mParent.notifyChildClose(this);
mParent = null;
mThreadGroup = null;
if(mEnv != null) {
mEnv.clear();
mEnv = null;
}
if(mSavedCD != null)
SystemEnvironment.getInstance().setProperty(
ShellConstants.PROP_USER_DIR, mSavedCD);
if(mSession != null) {
Util.safeRelease(mSession);
mSession = null;
}
getLogger().exit();
}
private void notifyChildClose(Shell shell) {
// Async code might invalidate job
// but removeJob can handle invalid pointers
ShellThread job = findJobByShell(shell);
if(job != null)
removeJob(job);
}
private ShellThread findJobByShell(Shell shell) {
List<ShellThread> children = getChildren(false);
if(children != null)
synchronized(children) {
for(ShellThread t : children) {
if(t.getShell() == shell)
return t;
}
}
return null;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#finalize()
*/
@Override
protected void finalize() throws Throwable {
getLogger().entry();
close();
}
public XEnvironment getEnv() {
return mEnv;
}
public SessionEnvironment getSession() {
return mSession;
}
public IExpression parseScript(Reader reader, String source)
throws CoreException {
getLogger().entry(reader, source);
try {
enterEval();
ShellParser parser = new ShellParser(this,
newParserReader(reader, true), source);
return parser.script();
} catch (Exception e) {
throw new CoreException("Exception parsing source: " + source, e);
}
finally {
exitEval();
}
}
public ICommandExpr parseEval(String scmd) throws CoreException {
getLogger().entry(scmd);
try (Reader reader = Util.toReader(scmd)) {
enterEval();
ShellParser parser = new ShellParser(this, newParserReader(reader, false),
"<eval>");
ICommandExpr c = parser.script();
return getLogger().exit(c);
} catch (Exception e) {
throw new CoreException("Exception parsing command: " + scmd, e);
}
finally {
exitEval();
}
}
private void exitEval() {
getLogger().entry();
int d;
if((d = mRunDepth.decrementAndGet()) < 0) {
getLogger().error("SNH: run depth underrun: " + d
+ " resetting to 0: stack {}", (Object[]) Thread
.currentThread().getStackTrace());
mRunDepth.compareAndSet(d, 0);
}
}
private void enterEval() {
mRunDepth.incrementAndGet();
}
public boolean isExecuting() {
return mRunDepth.get() > 0;
}
public boolean validateScript(Reader reader, String source)
throws ParseException, IOException {
SourceLocation saveLoc = getLocation();
ShellParser parser = null;
boolean bSuccess = false;
try {
enterEval();
parser = new ShellParser(this, newParserReader(reader, true), source);
while(parser.command_line() != null)
;
bSuccess = true;
} finally {
exitEval();
setCurrentLocation(saveLoc);
}
return bSuccess;
}
/*
*
* public int runScript( ) {
*
* ICommandExpr parsed = parseScript(reader, source)
*
*
* }
*/
public ReturnValue runScript(URL scriptURL, String source,
boolean convertReturn)
throws ParseException, IOException, ThrowException {
getLogger().entry(scriptURL, source, convertReturn);
try (Reader reader = Util.toReader(scriptURL, getInputTextEncoding())) {
return runScript(reader, source, convertReturn);
}
}
public ReturnValue runScript(Reader reader, String source,
boolean convertReturn) throws ParseException, ThrowException,
IOException {
getLogger().entry(reader, source, convertReturn);
try {
enterEval();
SourceLocation saveLoc = getLocation();
int exitStatus = 0;
ShellParser parser = null;
try {
parser = new ShellParser(this, newParserReader(reader, true), source);
while(!hasReturned()) {
CommandExpr c = parser.command_line();
if(c == null)
break;
setSourceLocation(c.getSourceLocation());
if(mOpts.mVerbose || mOpts.mLocation) {
if(mOpts.mLocation) {
getLogger().info(formatLocation());
}
if(mOpts.mVerbose) {
String s = c.describe(false);
if(s.length() > 0) {
printErr(s);
}
}
}
exitStatus = exec(c);
}
}
catch (ThrowException e) {
// mLogger.info("Rethrowing throw exception",e);
throw e;
} catch (ExitOnErrorException e) {
exitStatus = e.getValue();
}
catch (Exception | Error e) {
printErr(e.getMessage());
getLogger().warn("Exception parsing statement", e);
parser.ReInit(newParserReader(reader, false), source);
} finally {
setCurrentLocation(saveLoc);
}
// Exited evaluation - but still in entry point
if(mExitVal != null)
exitStatus = mExitVal.intValue();
// Special check for returning thrown errors
else if(convertReturn) // clears mReturnValue
exitStatus = getReturnValueAsExitStatus(exitStatus);
onSignal("EXIT");
return getLogger().exit(new ReturnValue(exitStatus, getReturnValue()));
} finally {
exitEval();
}
}
private boolean hasReturned() {
return !(mExitVal == null && mReturnVal == null);
}
private ShellParserReader newInteractiveParserReader()
throws IOException, CoreException {
return newParserReader(mIO.getReader(), false);
}
private ShellParserReader newParserReader(Reader reader, boolean bSkipBOM)
throws IOException {
return new ShellParserReader(reader, bSkipBOM);
}
public String getInputTextEncoding() {
return getSerializeOpts().getInputTextEncoding();
}
public int interactive() throws Exception {
mIsInteractive = true;
int ret = 0;
try {
enterEval();
// ShellParser parser= new
// ShellParser(mCommandInput,Shell.getEncoding());
ShellParser parser = new ShellParser(this, newInteractiveParserReader(),
"stdin");
while(mExitVal == null) {
CommandExpr c = null;
try {
parser.reset();
c = parser.command_line();
if(c == null)
break;
setSourceLocation(c.getSourceLocation());
if(mOpts.mVerbose) {
String s = c.describe(false);
if(s.length() > 0) {
SourceLocator loc = getLocation();
printErr("- " + s);
}
}
ret = exec(c);
// PrintWriter out = new PrintWriter( System.out );
// s.print(out);
// out.flush();
} catch (ThrowException e) {
printErr("Ignoring thrown value: " + e.getMessage());
getLogger().error("Ignoring throw value", e);
parser.ReInit(newInteractiveParserReader(), null);
} catch (Exception e) {
SourceLocation loc = c != null ? c.getSourceLocation() : null;
if(loc != null) {
String sLoc = loc.format(mOpts.mLocationFormat);
getLogger().info(loc.format(mOpts.mLocationFormat));
printErr(sLoc);
}
printErr(e.getMessage());
getLogger().warn("Exception parsing statement", e);
parser.ReInit(newInteractiveParserReader(), null);
} catch (Error e) {
printErr("Error: " + e.getMessage());
SourceLocation loc = c != null ? c.getSourceLocation() : null;
getLogger().warn("Exception parsing statement", e);
if(loc != null) {
String sLoc = loc.format(mOpts.mLocationFormat);
getLogger().info(loc.format(mOpts.mLocationFormat));
printErr(sLoc);
}
parser.ReInit(newInteractiveParserReader(), null);
}
}
if(mExitVal != null)
ret = mExitVal.intValue();
onSignal("EXIT");
} finally {
exitEval();
}
return ret;
}
public void setSourceLocation(SourceLocation loc) {
setCurrentLocation(loc);
}
public void runRC(String rcfile) throws IOException, Exception {
getLogger().entry(rcfile);
// Try to source the rcfile
if(rcfile != null) {
try {
enterEval();
File script = this.getFile(rcfile);
if(script.exists() && script.canRead()) {
ICommand icmd = CommandFactory.getScript(
this, script, rcfile, SourceMode.SOURCE, null);
if(icmd != null) {
// push location
SourceLocation l = getLocation();
setCurrentLocation(icmd.getLocation());
icmd.run(this, rcfile, null);
setCurrentLocation(l);
}
}
onSignal("EXIT");
} finally {
exitEval();
}
}
getLogger().exit();
}
public String getPS(String ps, String def) {
XValue ps1 = getEnv().getVarValue(ps);
if(ps1 == null)
return def;
String sps1 = ps1.toString();
if(!Util.isBlank(sps1))
try {
sps1 = EvalUtils.expandStringToString(this, sps1, mPSEnv);
} catch (IOException | CoreException e) {
getLogger().debug("Exception getting PS var " + ps, e);
return def;
}
return sps1;
}
/*
* Main entry point for executing commands. All command execution should go
* through this entry point
*
* Handles background shell ("&") Handles "throw on error" (-e)
*/
public int exec(ICommandExpr c) throws ThrowException, ExitOnErrorException {
return exec(c, getLocation(c));
}
// Default location for command
private SourceLocation getLocation(IExpression c) {
return c.hasLocation() ? c.getSourceLocation() : getLocation();
}
public int exec(ICommandExpr c, SourceLocation loc) throws ThrowException,
ExitOnErrorException {
getLogger().entry(c, loc);
try {
enterEval();
if(loc == null)
loc = c.getSourceLocation();
setCurrentLocation(loc);
if(mOpts.mExec || mOpts.mTrace) {
logExec(c, mOpts.mExec, loc);
}
try {
if(c.isWait()) {
// Execute forground command
mStatus = c.exec(this);
// If not success then may throw if option 'throw on error'
// is set (-e)
if(mStatus != 0 && mOpts.mThrowOnError && c.isSimple()) {
if(!isInCommandConndition())
throw new ExitOnErrorException(mStatus);
}
return mStatus;
}
ShellThread sht = new ShellThread(newThreadGroup(c.getName()),
new Shell(this), null, c);
if(isInteractive())
printErr("" + sht.getId());
addJob(sht);
sht.start();
return mStatus = 0;
} catch (ThrowException | ExitOnErrorException e) {
throw e;
}
catch (Exception e) {
printLoc(getLogger(), loc);
printErr("Exception running: " + c.describe(true));
printErr(e.toString(), loc);
getLogger().error(
"Exception running command: " + c.describe(false), e);
mStatus = -1;
// If not success then may throw if option 'throw on error' is
// set (-e)
if(mStatus != 0 && mOpts.mThrowOnError && c.isSimple()) {
if(!isInCommandConndition())
throw new ThrowException(XValue.newXValue(mStatus));
}
return getLogger().exit(mStatus);
}
} finally {
exitEval();
}
}
public void logExec(ICommandExpr c, boolean bExec, SourceLocation loc) {
StringBuilderWriter sw = new StringBuilderWriter();
PrintWriter w = new PrintWriter(sw);
c.print(w, bExec);
w.flush();
StringBuilder sb = sw.getBuilder();
if(sb.length() > 0) {
String scmd = sb.toString();
if(mOpts.mTrace && getLogger().isEnabled(mOpts.mTraceLevel)) {
traceExec(loc, scmd);
}
if(mOpts.mExec) {
if(loc != null) {
printErr("+ " + loc.format(mOpts.mLocationFormat));
printErr(scmd);
}
else
printErr("+ " + scmd);
}
}
}
/*
* Returns TRUE if the shell is currently in a condition
*/
private void traceExec(SourceLocation loc, String scmd) {
String strace = SourceLocation.formatLocation(loc,
mOpts.mLocationFormat) + " " + scmd;
if(mOpts.mTraceFile != null) {
String sEnc = mOpts.mSerialize.getOutputTextEncoding();
try {
OutputPort op = getEnv().getOutput(mOpts.mTraceFile, true);
try (OutputStream os = op.asOutputStream(getSerializeOpts())) {
os.write(strace.getBytes(sEnc));
os.write(Util.getNewlineBytes(getSerializeOpts()));
os.flush();
}
} catch (IOException | CoreException e) {
getLogger().debug("Exception tracing output", e);
}
}
else
getLogger().log(mOpts.mTraceLevel, strace);
}
public ThreadGroup newThreadGroup(String name) {
return new ThreadGroup(getThreadGroup(), name);
}
public boolean isInCommandConndition() {
return mConditionDepth > 0;
}
// Enter a condition
private void pushCondition() {
mConditionDepth++;
}
private void popCondition() {
mConditionDepth--;
}
public boolean isInteractive() {
return mIsInteractive;
}
private void addJob(ShellThread sht) {
List<ShellThread> children = getChildren(true);
synchronized(children) {
children.add(sht);
}
mLastThreadId = sht.getId();
}
public void printErr(String s) {
printErr(s, getLocation());
}
public void printErr(String s, SourceLocation loc) {
getLogger().entry(s, loc);
getLogger().warn("printOut: {}", s);
try (PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(getEnv().getStderr().asOutputStream(
getSerializeOpts()), getSerializeOpts()
.getOutputTextEncoding())))) {
if(mOpts.mLocation) {
out.println(formatLocation(loc));
}
out.println(s);
out.flush();
} catch (CoreException | IOException e) {
getLogger().error("Exception writing output: " + s, e);
} finally {
getLogger().exit();
}
}
private String formatLocation() {
return formatLocation(null);
}
private String formatLocation(SourceLocation loc) {
if(loc == null)
loc = getLocation();
return loc == null ? "<?>" : loc.format(mOpts.mLocationFormat);
}
public void printOut(String s) {
getLogger().entry(s);
getLogger().debug("printOut: {}", s);
try (PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(getEnv().getStdout().asOutputStream(
getSerializeOpts()), getSerializeOpts()
.getOutputTextEncoding())))) {
out.println(s);
out.flush();
} catch (IOException | CoreException e) {
getLogger().error("Exception writing output: " + s, e);
return;
} finally {
getLogger().exit();
}
}
public String readCommandLine(String prompt) throws IOException {
return mIO.readCommandLine(prompt);
}
public void printErr(String s, Exception e) {
getLogger().entry(s, e);
getLogger().warn("printOut: {}", s);
try (PrintWriter out = getEnv().getStderr().asPrintWriter(
getSerializeOpts())) {
SourceLocation loc = getLocation();
if(loc != null)
out.println(formatLocation(loc));
out.println(s);
out.println(e.getMessage());
for(Throwable t = e.getCause(); t != null; t = t.getCause()) {
out.println(" Caused By: " + t.getMessage());
}
out.flush();
} catch (IOException | CoreException e1) {
getLogger().error("Exception writing output: " + s, e);
return;
} finally {
getLogger().exit();
}
}
public static void main(String argv[]) throws Exception {
getLogger().entry();
List<XValue> vargs = new ArrayList<XValue>(argv.length);
for(String a : argv)
vargs.add(XValue.newXValue(a));
Shell shell = new Shell(new ShellIO(true));
org.xmlsh.builtin.commands.xmlsh cmd = new org.xmlsh.builtin.commands.xmlsh(
true);
int ret = -1;
try {
shell.enterEval();
ret = cmd.run(shell, "xmlsh", vargs);
} catch (ThrowException e) {
ret = shell.convertReturnValueToExitStatus(e.getValue());
} catch (Throwable e) {
getLogger().error("Uncaught exception in main", e);
} finally {
shell.exitEval();
shell.close();
}
getLogger().exit(ret);
System.exit(ret);
}
public void setArg0(String string) {
mArg0 = string;
}
// Translate a shell return code to java bool
public static boolean toBool(int intVal) {
return intVal == 0;
}
// Translate a java bool to a shell return code
public static int fromBool(boolean boolVal) {
return boolVal ? 0 : 1;
}
public SearchPath getExternalPath() {
return getPath(ShellConstants.PATH, true);
}
public SearchPath getPath(String var, boolean bSeqVar) {
XValue pathVar = getEnv().getVarValue(var);
if(pathVar == null || pathVar.isEmpty())
return new SearchPath();
if(bSeqVar)
return new SearchPath(pathVar);
else
return new SearchPath(pathVar.toString().split(File.pathSeparator));
}
/*
* Current Directory
*/
public static File getCurdir() {
return new File(System.getProperty(ShellConstants.PROP_USER_DIR));
}
public static Path getCurPath() {
return Paths.get(System.getProperty(ShellConstants.PROP_USER_DIR));
}
// While this can be static because of the thread-local
// use non-static to enforce per-shell behaviour
public void setCurdir(File cd) throws IOException {
String dir = cd.getCanonicalPath();
SystemEnvironment.getInstance().setProperty(
ShellConstants.PROP_USER_DIR, dir);
}
public void setArgs(List<XValue> args) {
mArgs = new CopyOnWriteArrayList<>(args);
}
public File getExplicitFile(String name, boolean mustExist)
throws IOException {
return getExplicitFile(null, name, mustExist);
}
public File getExplicitFile(String name, boolean mustExist, boolean isFile)
throws IOException {
File f = getExplicitFile(null, name, mustExist);
if(f != null && (isFile ? f.isFile() : f.isDirectory()))
return f;
return null;
}
public File getExplicitFile(String name, PathMatchOptions match)
throws IOException {
File f = getExplicitFile(null, name, true);
Path path = FileUtils.asValidPath(f);
if(path == null)
return null;
if(f != null && match.doVisit(path))
return f;
return null;
}
public File getExplicitFile(File dir, String name, boolean mustExist)
throws IOException {
File file = null;
file = new File(dir, name);
try {
file = file.getCanonicalFile();
} catch (IOException e) {
getLogger().info("Exception translating file to canonical file", e);
// try to still use file
}
if(mustExist && !file.exists())
return null;
return file;
}
public List<XValue> getArgs() {
return mArgs;
}
public void exit(int retval) {
mExitVal = Integer.valueOf(retval);
}
public void exec_return(XValue retval) {
mReturnVal = retval == null ? mReturnFlag : retval;
}
/*
* Return TRUE if we should keep running on this shell Includes early
* termination in control stacks
*/
public boolean keepRunning() {
// Hit exit stop
if(mClosed || hasReturned())
return false;
// If the top control stack is break then stop
if(hasControlStack()) {
ControlLoop loop = getControlStack().peek();
if(loop.mBreak || loop.mContinue)
return false;
}
return true;
}
public String getArg0() {
return mArg0;
}
/**
* @return the status
*/
public int getStatus() {
return mStatus;
}
/**
* @param status
* the status to set
*/
public void setStatus(int status) {
mStatus = status;
}
public File getFile(File dir, String file) throws IOException {
return getExplicitFile(dir, file, false);
}
public File getFile(String fname) throws IOException {
return getExplicitFile(fname, false);
}
public File getFile(XValue fvalue) throws IOException {
return getFile(fvalue.toString());
}
// unchecked exceptions
public Path getPath(XValue fvalue) throws InvalidPathException {
if(fvalue == null || fvalue.isNull())
return null;
return getPath(fvalue.toString());
}
public Path getPath(String fname) throws InvalidPathException {
Path dir = getCurPath();
return dir.resolve(fname);
}
public boolean shift(int num) {
num = Math.min(num, mArgs.size());
if(num <= 0)
return false;
mArgs = mArgs.subList(num, mArgs.size());
return true;
}
/*
* Returns the singleton processor for all of Xmlsh
*/
public static Processor getProcessor() throws IOException {
getLogger().entry();
Shell sh = ThreadLocalShell.get();
if(sh == null) {
try {
sh = new Shell();
} catch (CoreException e) {
Util.wrapIOException(e);
}
}
return sh.getLocalProcessor(null);
}
public synchronized Processor getLocalProcessor(Configuration config) {
getLogger().entry(config);
boolean init = false ;
if(config != null) {
if( mProcessor == null || !mProcessor.getUnderlyingConfiguration().equals(config)) {
getLogger().debug("different configurations processor {} old {} new {}" ,
mProcessor ,
mProcessor.getUnderlyingConfiguration(), config ) ;
mProcessor = new Processor(config);
init = true ;
}
}
if(mProcessor == null) {
getLogger().info("Allocating new processor");
String saxon_ee = getSystemProperty("XMLSH_SAXON_EE");
boolean bEE = Util.isEmpty(saxon_ee) ? true : Util
.parseBoolean(saxon_ee);
mProcessor = new Processor(bEE);
init = true ;
}
if( init ){
/*
* mProcessor.getUnderlyingConfiguration().getEditionCode();
*
* System.err.println("Version " +
* mProcessor.getSaxonProductVersion() );
* System.err.println("XQuery " +
* mProcessor.getConfigurationProperty
* FeatureKeys.XQUERY_SCHEMA_AWARE) ); System.err.println("XSLT " +
* mProcessor.getConfigurationProperty(FeatureKeys.XSLT_SCHEMA_AWARE) );
* System.err.println("Schema " +
* mProcessor.getConfigurationProperty(FeatureKeys.SCHEMA_VALIDATION
* ));
*/
// mProcessor.setConfigurationProperty(FeatureKeys.TREE_MODEL,
// net.sf.saxon.event.Builder.LINKED_TREE);
mProcessor.registerExtensionFunction(new EvalDefinition());
mProcessor.getUnderlyingConfiguration().setSerializerFactory(
new XmlshSerializerFactory(mProcessor
.getUnderlyingConfiguration()));
mProcessor.getUnderlyingConfiguration().setErrorListener(
new XmlshErrorListener());
}
return mProcessor;
}
public void removeJob(Thread job) {
if(mChildren != null)
synchronized(mChildren) {
mChildren.remove(job);
mChildren.notify(); // Must be in syncrhonized block
}
}
// Kill a child shell whether or not its in our list of children
public void killChild(ShellThread job, long waitTime) {
if(job != Thread.currentThread()) {
if(job.isAlive()) {
try {
job.shutdown(true, waitTime);
} catch (Exception e) {
getLogger().warn("Exception trying to close child shell: "
+ job.describe());
}
}
else
job.interrupt();
Thread.yield();
if(waitTime >= 0) {
try {
job.join(waitTime);
} catch (InterruptedException e) {
getLogger().warn("Exception trying to wait for shell: "
+ job.describe());
}
}
Thread.yield();
if(job.isAlive())
getLogger().warn("Failed to kill child shell: " + job.describe());
}
}
/*
* Returns the children of the current thread copied into a collection so
* that it is thread safe
*/
public List<ShellThread> getChildren(boolean create) {
// mChildren is only set never chaqnged or cleared
// Memory Barrier
if(mChildren == null && create) {
synchronized(this) {
if(mChildren == null)
mChildren = Collections
.synchronizedList(new ArrayList<ShellThread>());
}
}
return mChildren;
}
/*
* Waits until there are "at most n" running children of this shell
*/
public boolean waitAtMostChildren(int n, long waitTime) {
long end = System.currentTimeMillis() + waitTime;
waitTime = Util.nextWait(end, waitTime);
while(childrenSize() > n) {
if(waitTime < 0)
return false;
try {
enterEval(); // equivilent to an eval - were blocking
if(mChildren != null)
synchronized(mChildren) {
mChildren.wait(waitTime);
}
} catch (InterruptedException e) {
getLogger().warn("interrupted while waiting for job to complete", e);
} finally {
exitEval();
waitTime = Util.nextWait(end, waitTime);
}
}
return true;
}
private int childrenSize() {
// memory barrier
if(mChildren == null)
return 0;
synchronized(mChildren) {
return mChildren.size();
}
}
public long getLastThreadId() {
// TODO Auto-generated method stub
return mLastThreadId;
}
/*
* Break n levels of control stacks -1 means break all
*/
public int doBreak(int levels) {
if(!hasControlStack())
return 0;
int end = getControlStack().size() - 1;
if(levels < 0) {
while(end >= 0)
getControlStack().get(end--).mBreak = true;
}
else {
while(levels-- > 0 && end >= 0)
getControlStack().get(end--).mBreak = true;
}
return 0;
}
/*
* Continue n levels of control stacks
*/
public int doContinue(int levels) {
if(!hasControlStack())
return 0;
int end = getControlStack().size() - 1;
/*
* Break n-1 levels
*/
while(levels-- > 1 && end >= 0)
getControlStack().get(end--).mBreak = true;
// Continue the final level
if(end >= 0)
getControlStack().get(end).mContinue = true;
return 0;
}
public ControlLoop pushLoop(SourceLocator sourceLocator) {
ControlLoop loop = new ControlLoop(sourceLocator);
getControlStack().add(loop);
return loop;
}
/*
* Pop the control stack until we hit loop, if loop isnt found (SNH) pop
* until empty
*/
public void popLoop(ControlLoop loop) {
while(hasControlStack())
if(mControlStack.pop() == loop)
break;
}
public void declareFunction(IFunctionDefiniton func) {
getLogger().entry(func);
StaticContext ctx = getStaticContext();
assert (ctx != null);
ctx.declareFunction(func);
getLogger().exit();
}
public IFunctionDefiniton getFunctionDecl(String name) {
getLogger().entry(name);
// First look in the current module
// IFunctionDecl funcd = mModule.get().getFunction(name);
StaticContext ctx = getStaticContext();
assert (ctx != null);
return getLogger().exit(ctx.getFunction(name));
}
public Modules getModules() {
getLogger().entry();
return getLogger().exit(getEnv().getModules());
}
/*
* Execute a function as a command Extracts return values from the function
* if present
*/
public int execFunctionAsCommand(String name, ICommandExpr cmd,
SourceLocation location, List<XValue> args) throws Exception {
List<XValue> saveArgs = getArgs();
String saveArg0 = getArg0();
Variables save_vars = pushLocalVars();
getCallStack().push(new CallStackEntry(name, cmd, location));
setArg0(name);
setArgs(args);
try {
int ret = exec(cmd, location);
return ret;
} finally {
popLocalVars(save_vars);
setArg0(saveArg0);
setArgs(saveArgs);
getCallStack().pop();
}
}
/*
* Run a function expressed as a command body
*/
public XValue runCommandFunction(String name, ICommandExpr mBody,
List<XValue> args) throws ThrowException,
ExitOnErrorException {
List<XValue> saveArgs = getArgs();
String saveArg0 = getArg0();
Variables save_vars = pushLocalVars();
getCallStack().push(new CallStackEntry(name, mBody, getLocation()));
setArg0(name);
setArgs(args);
try {
int ret = exec(mBody, mBody.getSourceLocation());
return getReturnValue();
} finally {
popLocalVars(save_vars);
setArg0(saveArg0);
setArgs(saveArgs);
getCallStack().pop();
}
}
/*
* Convert return value to exit value
*/
private int convertReturnValueToExitStatus(XValue value) {
// Special case for null values
if(value == null || value == mReturnFlag)
return 0;
// Null is false (1)
if(value.isNull())
return 1;
// Change: Empty sequence is false
if(value.isEmpty() || value.isEmptySequence())
return 1;
/*
* Special: if looks like an integer then thats the exit value
*/
if(value.isAtomic()) {
// Check if native boolean
String s = value.toString();
// Empty string is false
if(Util.isBlank(s))
return 0;
if(Util.isInt(s, true))
return Integer.parseInt(s);
else if(s.equalsIgnoreCase("true"))
return 0;
else
return 1; // False
}
try {
return value.toBoolean() ? 0 : 1;
} catch (Exception e) {
getLogger().error("Exception parsing value as boolean", e);
return -1;
}
}
/*
* Declare a module using the prefix=value notation
*/
public boolean importModule(String prefix, String name, List<URL> at,
List<XValue> init)
throws Exception {
getLogger().entry(at, init);
PName pname = new PName(name);
ModuleConfig config = ModuleFactory.getModuleConfig(this, pname, at);
if(config == null)
return false;
IModule mod = getModules().getExistingModuleByName(config.getName());
if(mod == null)
mod = ModuleFactory.createModule(this, config);
if(mod == null)
throw new UnexpectedException("Module could not be created: " + name);
assert (mod != null);
boolean inited = getModules().importModule(this, prefix, mod, init);
if(inited)
getLogger().debug("Imported module {} fresh init: {}", mod, inited);
assert (mod != null);
return getLogger().exit(true);
}
// Dup function but may need different
public boolean importScript(String prefix, String name, List<URL> at,
List<XValue> init) throws Exception {
return importModule(prefix, name, at, init);
}
// returns true if succceeded
public boolean importPackage(String prefix, String name,
List<String> packages) throws Exception {
getLogger().entry(prefix, name, packages);
String sHelp = packages.get(0).replace(ShellConstants.kDOT_CHAR, '/')
+ "/commands.xml";
IModule mod = getModules().getExistingModuleByName(name);
if(mod == null) {
ModuleConfig config = ModuleFactory.getInternalModuleConfig(this,
name, packages, sHelp);
if(config != null)
mod = ModuleFactory.createPackageModule(this, config);
}
if(mod == null)
throw new FileNotFoundException("Cannot locate package module:" + name);
boolean inited = getModules().importModule(this, prefix, mod, null);
return true;
}
public void importJava(List<URL> urls) throws CoreException {
getLogger().entry(urls);
getModule().addClassPaths(this, urls);
return;
}
public URL getURL(String file) throws CoreException {
getLogger().entry(file);
URL url = Util.tryURL(file);
if(url == null)
try {
url = getFile(file).toURI().toURL();
} catch (IOException e) {
throw new CoreException(e);
}
return getLogger().exit(url);
}
public URI getURI(String file) throws CoreException {
URI uri = Util.tryURI(file);
if(uri == null)
try {
uri = getFile(file).toURI();
} catch (IOException e) {
throw new CoreException(e);
}
return uri;
}
public List<URL> toUrls(XValue args) throws CoreException {
if(args == null)
return null;
List<URL> at = new ArrayList<>();
for(XValue xv : args)
at.add(getURL(xv.toString()));
return at;
}
public List<URL> toUrls(List<XValue> args) throws CoreException {
if(args == null || args.isEmpty())
return null;
List<URL> at = new ArrayList<>(args.size());
for(XValue xv : args)
for(XValue x : xv)
at.add(getURL(x.toString()));
return at;
}
public InputPort newInputPort(String file) throws IOException {
/*
* Special case to support /dev/null file on Windows systems Doesnt hurt
* on unix either to fake this out instead of using the OS
*/
if(FileUtils.isNullFilePath(file)) {
return new StreamInputPort(new NullInputStream(), file);
}
URL url = Util.tryURL(file);
if(url != null) {
return new StreamInputPort(url.openStream(), url.toExternalForm());
}
else
return new FileInputPort(getFile(file));
}
// Returns a new port need to manage the port properly
public OutputPort newOutputPort(String file, boolean append)
throws FileNotFoundException, IOException {
if(FileUtils.isNullFilePath(file)) {
return new StreamOutputPort(new NullOutputStream());
}
else {
URL url = Util.tryURL(file);
if(url != null)
return new StreamOutputPort(url.openConnection()
.getOutputStream());
}
return new FileOutputPort(getFile(file), append);
}
public OutputStream getOutputStream(String file, boolean append,
SerializeOpts opts) throws FileNotFoundException, IOException,
CoreException {
OutputPort p = newOutputPort(file, append);
addAutoRelease(p);
return p.asOutputStream(opts);
}
public void addAutoRelease(OutputPort p) {
getEnv().addAutoRelease(p);
}
public OutputStream getOutputStream(File file, boolean append)
throws FileNotFoundException {
return new FileOutputStream(file, append);
}
public void setOption(String name, boolean flag) {
mOpts.setOption(name, flag);
}
public void setOption(String name, XValue value)
throws InvalidArgumentException {
mOpts.setOption(name, value);
}
public IModule getModule() {
getLogger().entry();
IModule mod = getEnv().getModule();
return getLogger().exit(mod);
}
/**
* @return the opts
*/
public ShellOpts getOpts() {
return mOpts;
}
/*
* Executes a command as a condition so that it doesnt throw an exception if
* errors
*/
public int execCondition(CommandExpr left) throws ThrowException,
ExitOnErrorException {
pushCondition();
try {
return exec(left);
}
finally {
popCondition();
}
}
/*
* Locate a resource in this shell, or in any of the modules
*/
public URL getResource(String res) {
URL url = getClass().getResource(res);
if(url != null)
return url;
for(IModule m : getModules()) {
url = m.getResource(res);
if(url != null)
return url;
}
return null;
}
public void setOptions(Options opts) throws InvalidArgumentException {
for(OptionValue ov : opts.getOpts()) {
setOption(ov);
}
}
private void setOption(OptionValue ov) throws InvalidArgumentException {
mOpts.setOption(ov);
}
public SerializeOpts getSerializeOpts(Options opts)
throws InvalidArgumentException {
if(opts == null || opts.getOpts() == null)
return mOpts.mSerialize;
SerializeOpts sopts = mOpts.mSerialize.clone();
sopts.setOptions(opts);
return sopts;
}
public SerializeOpts getSerializeOpts() {
return mOpts.mSerialize;
}
public Variables pushLocalVars() {
return mEnv.pushLocalVars();
}
public void popLocalVars(Variables vars) {
getLogger().entry(vars);
mEnv.popLocalVars(vars);
}
public boolean requireVersion(String module, String sreq) {
// Creates a 3-4 element array [ "1" , "0" , "1" , ? ]
String aver[] = Version.getVersion().split("\\.");
String areq[] = sreq.split("\\.");
// Start with major and go down
for(int i = 0; i < Math.max(aver.length, areq.length); i++) {
if(i >= areq.length)
break;
int ireq = Util.parseInt(areq[i], 0);
int iver = i >= aver.length ? 0 : Util.parseInt(aver[i], 0);
// Same version OK check minor
if(ireq == iver)
continue;
else if(ireq < iver)
break;
else if(ireq > iver) {
return false;
}
}
return true;
}
/*
* Get the return value of the last return statement
*/
private XValue getReturnValue() {
XValue ret = mReturnVal;
mReturnVal = null;
return (ret == null || ret == mReturnFlag) ? XValue.empytSequence()
: ret;
}
public XClassLoader getClassLoader() throws CoreException {
return getLogger().exit(getModule().getClassLoader());
}
public XClassLoader getClassLoader(final List<URL> urls)
throws CoreException {
return getClassLoader(urls, null);
}
public XClassLoader getClassLoader(final List<URL> urls,
XClassLoader parentClassLoader) throws CoreException {
getLogger().entry(urls);
// No class path sent, use this shells or this class
if(Util.isEmpty(urls)) {
XClassLoader loader = parentClassLoader == null ? getClassLoader()
: parentClassLoader;
return getLogger().exit(loader);
}
if(parentClassLoader == null)
parentClassLoader = getClassLoader();
if(parentClassLoader == null)
parentClassLoader = XClassLoader.newInstance(getContextClassLoader());
final ClassLoader parent = parentClassLoader;
getLogger().trace("Parent loader for new class loader: {}", parent);
return getLogger().exit(XClassLoader
.newInstance(urls.toArray(new URL[urls.size()]), parentClassLoader));
}
public void printLoc(Logger logger, SourceLocation loc) {
if(loc != null) {
String sLoc = loc.format(mOpts.mLocationFormat);
logger.info(sLoc);
}
}
public void trap(String signal, String cmd) {
if(Util.isBlank(signal) || Util.isBlank(cmd))
return;
if(mTraps == null)
mTraps = new HashMap<String, String>();
if(signal.equals("0"))
signal = "EXIT";
mTraps.put(signal, cmd);
}
void onSignal(String signal) {
// TODO: Should we avoid thiws on shutdown ?
if(mTraps == null)
return;
String scmd = mTraps.get(signal);
if(scmd == null)
return;
try {
ICommandExpr c = parseEval(scmd);
exec(c);
} catch (Exception e) {
this.printErr("Exception running trap: " + signal + ":" + scmd, e);
}
}
public SourceLocation getLocation() {
return mCurrentLocation == null ? new SourceLocation()
: mCurrentLocation;
}
public SourceLocation getLocation(int depth) {
if(depth < 0)
return getLocation();
if(!hasCallStack())
return null;
if(mCallStack.size() <= depth)
return null;
return mCallStack.get(depth).makeLocation();
}
public synchronized Shell getParent() {
return mParent;
}
private boolean hasControlStack() {
return mControlStack != null && !mControlStack.empty();
}
private Stack<ControlLoop> getControlStack() {
if(mControlStack == null)
mControlStack = new Stack<ControlLoop>();
return mControlStack;
}
public boolean hasCallStack() {
return mCallStack != null && !mCallStack.empty();
}
public Stack<CallStackEntry> getCallStack() {
if(mCallStack == null)
mCallStack = new Stack<CallStackEntry>();
return mCallStack;
}
public int getReturnValueAsExitStatus(int defRet) {
// Special null return value means 0 - SNH need to check logic of
// scripts
if(mReturnVal == null)
return defRet;
return convertReturnValueToExitStatus(getReturnValue());
}
public void setCurrentLocation(SourceLocation loc) {
if(loc == null && mCurrentLocation != null)
getLogger().debug("Overriting current location with null");
else
mCurrentLocation = loc;
}
public IFS getIFS() {
String sisf = getEnv().getVarString("IFS");
if(mIFS == null || !mIFS.isCurrent(sisf))
;
mIFS = new IFS(sisf);
return mIFS;
}
public ThreadGroup getThreadGroup() {
return mThreadGroup == null ? Thread.currentThread().getThreadGroup()
: mThreadGroup;
}
public ShellThread getFirstThreadChild() {
if(mChildren == null)
return null;
synchronized(mChildren) {
return mChildren.isEmpty() ? null : mChildren.get(0);
}
}
// Shutdown the shell
// Intended to be called from a external thread to try to force the shell to
// quit
// Dont actually close it - causes too much asyncronous grief
public boolean shutdown(boolean force, long waitTime) {
getLogger().entry(force, waitTime);
if(mClosed)
return getLogger().exit(true);
// Mark us done
mExitVal = Integer.valueOf(0);
long end = System.currentTimeMillis() + waitTime;
waitTime = Util.nextWait(end, waitTime);
// Break all
doBreak(-1);
List<ShellThread> children = getChildren(false);
if(force) {
if(children != null) {
synchronized(children) {
children = new ArrayList<>(children);
}
for(ShellThread c : children) {
waitTime = Util.nextWait(end, waitTime);
killChild(c, waitTime);
}
}
terminateChildProcesses();
}
waitTime = Util.nextWait(end, waitTime);
waitAtMostChildren(0, waitTime);
Thread.yield();
return getLogger().exit(mClosed);
}
public void addChildProcess(Process proc) {
if(mChildProcess == null) {
synchronized(this) {
if(mChildProcess == null)
mChildProcess = Collections
.synchronizedList(new ArrayList<Process>());
}
}
synchronized(mChildProcess) {
mChildProcess.add(proc);
}
}
public void removeChildProcess(Process proc) {
if(mChildProcess == null)
return;
mChildProcess.remove(proc);
}
public void terminateChildProcesses() {
if(mChildProcess == null)
return;
synchronized(mChildProcess) {
for(Process p : mChildProcess)
p.destroy();
mChildProcess.clear();
}
}
public boolean isClosed() {
return mClosed;
}
public IModule getModuleByPrefix(String prefix) {
return getLogger().exit(getEnv().getModuleByPrefix(prefix));
}
public FunctionDefinitions getFunctionDelcs() {
return getEnv().getFunctions();
}
/*
* Associates the current shell with an associated module and any static
* context
* it may have from when it was initialized.
*/
public StaticContext getStaticContext() {
getLogger().entry();
return getLogger().exit(getEnv().getStaticContext());
}
public Iterable<IModule> getDefaultModules() {
getLogger().entry();
return getStaticContext().getDefaultModules();
}
public StaticContext getExportedContext() {
getLogger().entry();
return getLogger().exit(mEnv.exportStaticContext());
}
public void pushModule(IModule mod) {
ThreadContext.put("xmodule", mod.describe());
getLogger().entry(mod);
getLogger().exit(getEnv().pushModule(mod, mod.getStaticContext()));
}
public IModule popModule() throws IOException {
getLogger().entry();
getEnv().popModule();
IModule mod = getModule();
ThreadContext.put("xmodule", mod.describe());
return getLogger().exit(mod);
}
public static ClassLoader getContextClassLoader() {
getLogger().entry();
/*
* Gets the shell context class loadeder
*/
try {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if(loader == null)
loader = ClassLoader.getSystemClassLoader();
return getLogger().exit(loader);
} catch (Throwable t) {
getLogger().catching(t);
return getLogger().exit(ClassLoader.getSystemClassLoader());
}
}
@Override
public String getPrompt(int level) {
switch(level){
case 0:
return getPS(ShellConstants.PS1, "$ ");
case 1:
return getPS(ShellConstants.PS2, "> ");
default:
return null;
}
}
public String getShellHome() {
XValue h = getEnv().getVarValue(ShellConstants.ENV_XMLSH_HOME);
if(h == null || h.isEmpty())
return null;
return FileUtils.toJavaPath(getPath(h));
}
public SearchPath getModulePath() {
String home = getShellHome();
SearchPath xm = null;
if(home != null)
xm = new SearchPath(Paths.get(home, "modules"));
else
xm = new SearchPath();
xm.add(getPath(ShellConstants.ENV_XMODPATH, true));
return xm;
}
/*
* Saxon Integraton
* (non-Javadoc)
*
* @see net.sf.saxon.lib.Initializer#initialize(net.sf.saxon.Configuration)
*/
@Override
public void initialize(Configuration config) throws TransformerException {
getLogger().debug("initialization {} ", config);
config.getLogger().info("initialze in xmlsh: " + this.toString());
config.registerExtensionFunction(new EvalDefinition());
}
public static void postInit(String string) {
getLogger().info("Initialized logging");
}
}
//
//
// 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.
//