/**
* $Id$
* $Date$
*
*/
package org.xmlsh.sh.shell;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
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 net.sf.saxon.lib.FeatureKeys;
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.XdmEmptySequence;
import net.sf.saxon.s9api.XdmItem;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.xmlsh.core.CommandFactory;
import org.xmlsh.core.CoreException;
import org.xmlsh.core.ExitOnErrorException;
import org.xmlsh.core.FileInputPort;
import org.xmlsh.core.FileOutputPort;
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.OutputPort;
import org.xmlsh.core.Path;
import org.xmlsh.core.StreamInputPort;
import org.xmlsh.core.StreamOutputPort;
import org.xmlsh.core.ThrowException;
import org.xmlsh.core.Variables;
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.sh.core.Command;
import org.xmlsh.sh.core.FunctionDeclaration;
import org.xmlsh.sh.core.SourceLocation;
import org.xmlsh.sh.grammar.ParseException;
import org.xmlsh.sh.grammar.ShellParser;
import org.xmlsh.sh.grammar.ShellParserReader;
import org.xmlsh.util.NullInputStream;
import org.xmlsh.util.NullOutputStream;
import org.xmlsh.util.SessionEnvironment;
import org.xmlsh.util.Util;
import org.xmlsh.xpath.EvalDefinition;
import org.xmlsh.xpath.ShellContext;
public class Shell {
private static Logger mLogger = LogManager.getLogger(Shell.class);
private ShellOpts mOpts;
private FunctionDefinitions mFunctions = null;
private XEnvironment mEnv = null;
private List<XValue> mArgs = new ArrayList<XValue>();
private InputStream mCommandInput = null;
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 List<ShellThread> mChildren = new ArrayList<ShellThread>();
private Map<String,String> mTraps = null ;
private boolean mIsInteractive = false ;
private long mLastThreadId = 0;
private Stack<ControlLoop> mControlStack = new Stack<ControlLoop>();
// Depth of conditions used for 'throw on error'
private int mConditionDepth = 0;
private Modules mModules = null;
// current module
private Module mModule = null;
// Current classloader
private ClassLoader mClassLoader = null ;
private SourceLocation mCurrentLocation = null ;
/*
* Initializtion statics
*/
private static boolean bInitialized = false ;
private static Properties mSavedSystemProperties;
private static Processor mProcessor = null;
// private static ModuleURIResolver mModuleURIResolver = null ;
private Shell mParent = null;
/**
* Must call initialize atleast once, protects against multiple initializations
*/
public static void initialize()
{
if( bInitialized )
return ;
String logging = System.getenv("XDISABLE_LOGGING");
Logging.configureLogger(Util.parseBoolean(logging) );
mLogger.info("xmlsh initialize");
/*
* Workaround a saxon bug - pre-initialize processor
*/
// getProcessor();
// Can only be called once per process
try {
URL.setURLStreamHandlerFactory(new ShellURLFactory() );
}
catch( Error e )
{
// mLogger.debug("Exception trying to seURLStreamHandlerFactory" , e );
}
mSavedSystemProperties = System.getProperties();
SystemEnvironment.getInstance().setProperty("user.dir", System.getProperty("user.dir"));
System.setProperties( new SystemProperties(System.getProperties()));
// PropertyConfigurator.configure(Shell.class.getResource("log4j.properties"));
}
public static void uninitialize()
{
if( ! bInitialized )
return ;
mProcessor = null ;
System.setProperties( mSavedSystemProperties );
mSavedSystemProperties = null ;
SystemEnvironment.uninitialize();
ShellContext.set(null);
bInitialized = false ;
}
static {
initialize();
}
/*
* New top level shell
*/
public Shell( ) throws IOException, CoreException
{
this( true );
}
public Shell(boolean bUseStdio) throws IOException, CoreException
{
mOpts = new ShellOpts();
mSavedCD = System.getProperty("user.dir");
mEnv = new XEnvironment(this,bUseStdio);
mModules = new Modules();
mSession = new SessionEnvironment();
// Add xmlsh commands
mModules.declare( new Module( null , "xmlsh" , "org.xmlsh.commands.internal", CommandFactory.kCOMMANDS_HELP_XML));
setGlobalVars();
mModule = null ; // no current module
ShellContext.set(this); // cur thread active shell
}
/*
* Populate the environment with any global variables
*/
private void setGlobalVars() throws InvalidArgumentException {
Map<String,String> env = System.getenv();
for( Map.Entry<String,String > entry : env.entrySet() ){
String name = entry.getKey();
if( Util.isPath(name) )
continue ;
if( Util.isBlank(name))
continue ;
if( ! name.matches("^[a-zA-Z_0-9]+$"))
continue ;
// Ignore PS1
if( name.equals("PS1"))
continue ;
getEnv().setVar( new XVariable( name , new XValue(entry.getValue()) , EnumSet.of(XVarFlag.EXPORT )),false );
}
// Export path to shell path
String path = Util.toJavaPath(System.getenv("PATH"));
getEnv().setVar( new XVariable("PATH",
Util.isBlank(path) ? new XValue() : new XValue(path.split(File.pathSeparator))) , false );
String xpath = Util.toJavaPath(System.getenv("XPATH"));
getEnv().setVar( new XVariable("XPATH",
Util.isBlank(xpath) ? new XValue(".") : new XValue(xpath.split(File.pathSeparator))) , false );
String xmpath = Util.toJavaPath(System.getenv("XMODPATH"));
getEnv().setVar( new XVariable("XMODPATH",
Util.isBlank(xmpath) ? new XValue() : new XValue(xmpath.split(File.pathSeparator))) , false );
// PWD
getEnv().setVar(
new XDynamicVariable("PWD" , EnumSet.of( XVarFlag.READONLY , XVarFlag.XEXPR )) {
public XValue getValue()
{
return new XValue( Util.toJavaPath(getEnv().getCurdir().getAbsolutePath()) ) ;
}
}
, false
);
// RANDOM
getEnv().setVar(
new XDynamicVariable("RANDOM" , EnumSet.of( XVarFlag.READONLY , XVarFlag.XEXPR )) {
Random mRand = new Random();
public XValue getValue()
{
return new XValue( mRand.nextInt(0x7FFF) );
}
}
, false
);
// RANDOM32
getEnv().setVar(
new XDynamicVariable("RANDOM32" , EnumSet.of( XVarFlag.READONLY , XVarFlag.XEXPR )) {
Random mRand = new Random();
public XValue getValue()
{
long v = mRand.nextInt();
v &= 0x7FFFFFFFL;
return new XValue( (int) v );
}
}
, false
);
// RANDOM
getEnv().setVar(
new XDynamicVariable("RANDOM64" , EnumSet.of( XVarFlag.READONLY , XVarFlag.XEXPR )) {
Random mRand = new Random();
public XValue getValue()
{
return new XValue( mRand.nextLong() & 0x7FFFFFFFFFFFFFFFL );
}
}
, false
);
getEnv().setVar("TMPDIR" , Util.toJavaPath(System.getProperty("java.io.tmpdir")), false );
if( getEnv().getVar("HOME") == null )
getEnv().setVar("HOME" , Util.toJavaPath(System.getProperty("user.home")), false );
}
/*
* Cloned shell for sub-thread execution
*/
private Shell( Shell that ) throws IOException
{
mParent = that;
mOpts = new ShellOpts(that.mOpts);
mEnv = that.getEnv().clone(this) ;
mCommandInput = that.mCommandInput;
mArg0 = that.mArg0;
// clone $1..$N
mArgs = new ArrayList<XValue>();
mArgs.addAll(that.mArgs);
mSavedCD = System.getProperty("user.dir");
if( that.mFunctions != null )
mFunctions = new FunctionDefinitions(that.mFunctions);
mModules = new Modules(that.mModules );
mModule = that.mModule;
// Pass through the Session Enviornment, keep a reference
mSession = that.mSession;
mSession.addRef();
// Reference the parent classloader
mClassLoader = that.mClassLoader;
// Cloning shells doesnt save the condition depth
// mConditionDepth = that.mConditionDepth;
}
public Shell clone()
{
try {
return new Shell( this );
} catch (IOException e) {
printErr("Exception cloning shell",e);
return null;
}
}
public void close()
{
try {
if( mEnv != null ){
mEnv.close();
mEnv = null ;
}
if( mSavedCD != null )
SystemEnvironment.getInstance().setProperty("user.dir", mSavedCD);
if( mSession != null ){
mSession.release();
mSession = null ;
}
} catch (CoreException e) {
mLogger.error("Exception closing shell" , e);
}
}
/* (non-Javadoc)
* @see java.lang.Object#finalize()
*/
@Override
protected void finalize() throws Throwable {
close();
}
public XEnvironment getEnv() {
return mEnv;
}
public SessionEnvironment getSession()
{
return mSession ;
}
public Command parseEval( String scmd ) throws CoreException
{
InputStream save = mCommandInput;
InputStream is = null;
try {
is = Util.toInputStream(scmd, getSerializeOpts());
mCommandInput = is ;
ShellParser parser= new ShellParser(new ShellParserReader(mCommandInput,getInputTextEncoding()));
Command c = parser.script();
return c;
} catch ( Exception e )
{
throw new CoreException("Exception parsing command: " + scmd , e );
}
finally {
mCommandInput = save;
Util.safeClose(is);
}
}
public int runScript( InputStream stream, String source, boolean convertReturn ) throws ParseException, ThrowException, IOException
{
InputStream save = mCommandInput;
mCommandInput = stream ;
ShellParser parser= new ShellParser(new ShellParserReader(mCommandInput,getInputTextEncoding(),true), source );
int ret = 0;
try {
while( mExitVal == null && mReturnVal == null ){
Command c = parser.command_line();
if( c == null )
break;
if( mOpts.mVerbose ){
String s = c.toString(false);
if( s.length() > 0){
SourceLocation loc = c.getLocation();
if( loc != null ){
String sLoc = loc.toString();
mLogger.info(sLoc);
printErr( "- " + sLoc );
printErr(s );
} else
printErr( "- " + s );
}
}
if( mOpts.mNoExec)
continue ;
ret = exec( c );
}
}
catch( ThrowException e )
{
// mLogger.info("Rethrowing throw exception",e);
throw e ; // rethrow
}
catch( ExitOnErrorException e )
{
// Caught Exit on error from a script
ret = e.getValue();
}
catch (Exception e) {
// System.out.println("NOK.");
printErr(e.getMessage());
mLogger.error("Exception parsing statement" , e );
parser.ReInit(new ShellParserReader(mCommandInput,getInputTextEncoding()), source );
} catch (Error e) {
printErr(e.getMessage());
mLogger.error("Exception parsing statement" , e );
parser.ReInit(new ShellParserReader(mCommandInput,getInputTextEncoding()), source);
}
finally {
mCommandInput = save;
}
if( mExitVal != null )
ret = mExitVal.intValue();
else
if( convertReturn && mReturnVal != null ){
try {
ret = mReturnVal.toBoolean() ? 0 : 1;
} catch (Exception e) {
mLogger.error("Exception converting return value to boolean", e );
ret = -1;
}
mReturnVal = null ;
}
onSignal("EXIT");
return ret;
}
public String getInputTextEncoding() {
return getSerializeOpts().getInputTextEncoding();
}
public int interactive() throws Exception
{
mIsInteractive = true ;
int ret = 0;
setCommandInput();
// ShellParser parser= new ShellParser(mCommandInput,Shell.getEncoding());
ShellParser parser= new ShellParser(new ShellParserReader(mCommandInput,getInputTextEncoding()));
while (mExitVal == null) {
System.out.print(getPS1());
Command c = null ;
try {
c = parser.command_line();
if( c == null )
break;
if( mOpts.mVerbose ){
String s = c.toString(false);
if( s.length() > 0){
SourceLocation loc = c.getLocation();
if( loc != null ) {
printErr("- " + loc.toString());
printErr(s);
} else
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());
mLogger.error("Ignoring throw value",e);
parser.ReInit(new ShellParserReader(mCommandInput,getInputTextEncoding()));
}
catch (Exception e) {
SourceLocation loc = c != null ? c.getLocation() : null ;
if( loc != null ){
String sLoc = loc.toString();
mLogger.info(loc.toString());
printErr( sLoc );
}
printErr(e.getMessage());
mLogger.error("Exception parsing statement",e);
parser.ReInit(new ShellParserReader(mCommandInput,getInputTextEncoding()));
} catch (Error e) {
printErr("Error: " + e.getMessage());
SourceLocation loc = c != null ? c.getLocation() : null ;
mLogger.error("Exception parsing statement",e);
if( loc != null ){
String sLoc = loc.toString();
mLogger.info(loc.toString());
printErr( sLoc );
}
parser.ReInit(new ShellParserReader(mCommandInput,getInputTextEncoding()));
}
}
if( mExitVal != null )
ret = mExitVal.intValue();
onSignal("EXIT");
return ret;
}
public void setSourceLocation(SourceLocation loc)
{
mCurrentLocation = loc ;
}
public void runRC(String rcfile) throws IOException, Exception {
// Try to source the rcfile
if( rcfile != null ){
File script = this.getFile(rcfile);
if( script.exists() && script.canRead() ){
ICommand icmd = CommandFactory.getInstance().getScript(this, script ,true, null );
if( icmd != null ){
SourceLocation l = mCurrentLocation ;
mCurrentLocation = icmd.getLocation();
icmd.run(this, rcfile , null);
mCurrentLocation = l;
}
}
onSignal("EXIT");
}
}
public String getPS1() throws IOException, CoreException {
XValue ps1 = getEnv().getVarValue("PS1");
if( ps1 == null )
return "$ ";
String sps1 = ps1.toString();
if( !Util.isBlank(sps1))
sps1 = expandString(sps1, false,null);
return sps1;
}
/*
* Setup the mCommandInput
* Try to locate jline if its in the classpath and use it
* otherwise default to System.in
*/
private void setCommandInput() {
mCommandInput = null ;
if( ! Util.isWindows() )
try {
/*
* import jline.ConsoleReader;
* import jline.ConsoleReaderInputStream;
*/
Class<?> consoleReaderClass = Class.forName("jline.ConsoleReader");
if(consoleReaderClass != null ){
Class<?> consoleInputClass = Class.forName("jline.ConsoleReaderInputStream");
if( consoleInputClass != null ){
// ConsoleReader jline = new ConsoleReader();
Object jline = consoleReaderClass.newInstance();
Constructor<?> constructor = consoleInputClass.getConstructor( consoleReaderClass );
// mCommandInput = new ConsoleReaderInputStream(jline);
if( constructor != null ){
mCommandInput = (InputStream) constructor.newInstance(jline);
// System.err.println("using jline");
}
}
}
} catch (Exception e1) {
mLogger.info("Exception loading jline");
}
if( mCommandInput == null )
mCommandInput = System.in;
}
/*
* 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(Command c) throws ThrowException, ExitOnErrorException {
return exec(c,null);
}
public int exec(Command c, SourceLocation loc) throws ThrowException, ExitOnErrorException {
if( loc == null )
loc = c.getLocation();
mCurrentLocation = loc ;
if( mOpts.mExec){
String out = c.toString(true);
if( out.length() > 0 ){
if( loc != null ) {
printErr("+ " + loc.toString());
printErr( out );
}
else
printErr("+ " + out);
}
}
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( new Shell(this) , this , c);
if( isInteractive() )
printErr( "" + sht.getId() );
addJob( sht );
sht.start();
return mStatus = 0;
}
catch( ThrowException e ){
// mLogger.info("Rethrowing ThrowException",e);
throw e ;
}
catch( ExitOnErrorException e ){
// rethrow
throw e ;
}
catch( Exception e )
{
printLoc( mLogger , loc );
printErr("Exception running: " + c.toString(true) );
printErr(e.toString());
mLogger.error("Exception running command: " + c.toString(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( new XValue(mStatus));
}
return mStatus ;
}
}
/*
* Returns TRUE if the shell is currently in a condition
*/
public boolean isInCommandConndition() {
return mConditionDepth > 0 ;
}
// Enter a condition
private void pushCondition()
{
mConditionDepth++;
}
private void popCondition()
{
mConditionDepth--;
}
private boolean isInteractive() {
return mIsInteractive ;
}
private synchronized void addJob(ShellThread sht) {
mChildren.add(sht);
mLastThreadId = sht.getId();
}
public void printErr(String s) {
PrintWriter out;
try {
out = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(getEnv().getStderr().asOutputStream(getSerializeOpts()), getSerializeOpts().getOutputTextEncoding())
)
);
} catch (IOException e) {
mLogger.error("Exception printing err:" + s , e );
return ;
}
out.println(s);
out.flush();
out.close();
}
public void printOut(String s) {
PrintWriter out;
try {
out = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(getEnv().getStdout().asOutputStream(getSerializeOpts()), getSerializeOpts().getOutputTextEncoding())
)
);
} catch (IOException e) {
mLogger.error("Exception writing output: " + s , e );
return;
}
out.println(s);
out.flush();
out.close();
}
public void printErr(String s,Exception e) {
PrintWriter out;
try {
out = getEnv().getStderr().asPrintWriter(getSerializeOpts());
} catch (IOException e1) {
mLogger.error("Exception writing output: " + s , e );
return ;
}
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();
out.close();
}
public static void main(String argv[]) throws Exception {
List<XValue> vargs = new ArrayList<XValue>(argv.length);
for( String a : argv)
vargs.add( new XValue(a));
org.xmlsh.commands.builtin.xmlsh cmd = new org.xmlsh.commands.builtin.xmlsh(true);
Shell shell = new Shell();
int ret = -1;
try {
ret = cmd.run(shell , "xmlsh" , vargs);
} finally {
shell.close();
}
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 Path getExternalPath(){
return getPath("PATH",true);
}
public Path getPath(String var, boolean bSeqVar ){
XValue pathVar = getEnv().getVarValue(var);
if( pathVar == null )
return new Path();
if( bSeqVar )
return new Path( pathVar );
else
return new Path( pathVar.toString().split( File.pathSeparator ));
}
/*
* Current Directory
*/
public File getCurdir()
{
return new File( System.getProperty("user.dir"));
}
public void setCurdir( File cd ) throws IOException
{
String dir = cd.getCanonicalPath();
SystemEnvironment.getInstance().setProperty("user.dir",dir);
}
public void setArgs(List<XValue> args) {
mArgs = args ;
}
public File getExplicitFile(String name, boolean mustExist ) throws IOException {
return getExplicitFile( null , name,mustExist );
}
public File getExplicitFile(File dir , String name, boolean mustExist ) throws IOException {
File file=null;
try {
file = new File( dir , name).getCanonicalFile();
if( mustExist && ! file.exists() )
return null;
}
catch( IOException e ){
// Ignore IOExceptions trying to get a file because it is typically
// an invalid name like foo:bar
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 ;
}
/*
* Return TRUE if we should keep running on this shell
* Includes early termination in control stacks
*/
public boolean keepRunning()
{
// Hit exit stop
if( mExitVal != null || mReturnVal != null )
return false ;
// If the top control stack is break then stop
if(! mControlStack.empty() ){
ControlLoop loop = mControlStack.peek();
if( loop.mBreak || loop.mContinue )
return false;
}
return true ;
}
public String getArg0() {
return mArg0;
}
public List<XValue> expand(String s, boolean bExpandSequences , boolean bExpandWild , boolean bExpandWords , boolean bTongs , SourceLocation loc ) throws IOException, CoreException {
Expander e = new Expander( this , loc );
List<XValue> result = e.expand(s,bExpandWild, bExpandWords, bTongs );
if( bExpandSequences )
result = Util.expandSequences( result );
else
result = Util.combineSequence( result );
return result;
}
/**
* @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());
}
public String expandString(String value, boolean bExpandWild , SourceLocation loc ) throws IOException, CoreException {
List<XValue> ret = expand(value,false,bExpandWild, false, false , loc );
if( ret.size() == 0 )
return "";
else
if( ret.size() == 1 )
return ret.get(0).toString();
StringBuffer sb = new StringBuffer();
for( XValue v : ret ){
if( sb.length() > 0 )
sb.append(' ');
sb.append( v.toString() );
}
return sb.toString();
}
// Expand a word and return as a single XValue
// Preserves sequences and expands
public XValue expand( String value , boolean bExpandWild , boolean bExpandWords , boolean bTongs , SourceLocation loc ) throws IOException, CoreException {
List<XValue> ret = expand(value,false, bExpandWild , bExpandWords, bTongs , loc );
if( ret.size() == 0 )
return new XValue(XdmEmptySequence.getInstance());
else
if( ret.size() == 1 )
return ret.get(0);
return new XValue( ret );
}
public void shift(int num) {
while( ! mArgs.isEmpty() && num-- > 0 )
mArgs.remove(0);
}
/*
* Returns the singleton processor for all of Xmlsh
*/
public static synchronized Processor getProcessor()
{
if( mProcessor == null ){
String saxon_ee = System.getenv("XMLSH_SAXON_EE");
boolean bEE = Util.isEmpty(saxon_ee) ? true : Util.parseBoolean(saxon_ee);
mProcessor = new Processor( bEE );
mProcessor.setXmlVersion("1.1");
mProcessor.setConfigurationProperty(FeatureKeys.XQUERY_VERSION, "3.0");
/*
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 synchronized void removeJob(ShellThread job) {
mChildren.remove(job);
notify();
}
/*
* Returns the children of the current thread
* copied into a collection so that it is thread safe
*/
public synchronized List<ShellThread> getChildren()
{
ArrayList<ShellThread> copy = new ArrayList<ShellThread>();
copy.addAll(mChildren);
return copy;
}
/*
* Waits until there are "at most n" running children of this shell
*/
public synchronized void waitAtMostChildren(int n)
{
while( mChildren.size() > n ){
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public long getLastThreadId() {
// TODO Auto-generated method stub
return mLastThreadId;
}
/*
* Break n levels of control stacks
*/
public int doBreak(int levels)
{
int end = mControlStack.size() - 1 ;
while( levels-- > 0 && end >= 0 )
mControlStack.get(end--).mBreak = true ;
return 0;
}
/*
* Continue n levels of control stacks
*
*/
public int doContinue(int levels)
{
int end = mControlStack.size() - 1 ;
/*
* Break n-1 levels
*/
while( levels-- > 1 && end >= 0 )
mControlStack.get(end--).mBreak = true ;
// Continue the final level
if( end >= 0 )
mControlStack.get(end).mContinue = true ;
return 0;
}
public ControlLoop pushLoop(SourceLocation sourceLocation) {
ControlLoop loop = new ControlLoop(sourceLocation);
mControlStack.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( ! mControlStack.empty() )
if ( mControlStack.pop() == loop )
break ;
}
public void declareFunction(FunctionDeclaration func) {
if( mFunctions == null )
mFunctions = new FunctionDefinitions();
mFunctions.put( func.getName() , func);
}
public FunctionDeclaration getFunction(String name) {
if( mFunctions == null )
return null;
return mFunctions.get(name);
}
public Modules getModules() {
return mModules;
}
/*
* Execute a command as a function body
* Extracts return values from the function if present
*/
public int execFunction(Command body) throws Exception {
int ret = exec(body);
if( mReturnVal != null ){
ret = convertReturnValueToExitValue( mReturnVal) ;
mReturnVal = null;
}
return ret ;
}
/*
* Convert return value to exit value
*/
private int convertReturnValueToExitValue(XValue value) {
// Null is true (0)
if( value.isNull() )
return 0;
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.equals("true"))
return 0;
else
return 1; // False
}
// Non atomic
try {
return value.toBoolean() ? 0 : 1 ;
} catch (Exception e) {
mLogger.error("Exception parsing value as boolean",e);
return -1;
}
}
/*
* Declare a module using the prefix=value notation
*/
public void importModule(String moduledef, List<XValue> init) throws CoreException {
mModules.declare(this, moduledef, init );
}
public void importPackage(String prefix ,String name , String pkg ) throws CoreException {
String sHelp = pkg.replace('.', '/') + "/commands.xml";
mModules.declare( new Module( prefix , name , pkg , sHelp));
}
public void importJava( XValue uris ) throws CoreException
{
mClassLoader = getClassLoader( uris );
}
public URL getURL( String file ) throws CoreException
{
URL url = Util.tryURL(file);
if( url == null )
try {
url = getFile(file).toURI().toURL();
} catch (MalformedURLException e) {
throw new CoreException( e );
} catch (IOException e) {
throw new CoreException(e);
}
return url;
}
public URI getURI( String file ) throws MalformedURLException, IOException
{
URI uri = Util.tryURI(file);
if( uri == null )
uri = getFile(file).toURI();
return uri;
}
public InputPort getInputPort(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( file.equals("/dev/null") ){
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));
}
public OutputPort getOutputPort(String file, boolean append) throws FileNotFoundException, IOException
{
if( file.equals("/dev/null")){
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
{
return getOutputPort( file , append).asOutputStream(opts);
}
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 void setModule(Module module) {
mModule = module ;
}
public Module getModule() {
return mModule ;
}
/**
* @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(Command 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( Module m : mModules ){
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 ) {
mEnv.popLocalVars( vars );
}
public int 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 -1 ;
}
}
return 0;
}
/*
* Get the return value of the last return statement
*/
public XValue getReturnValue(boolean bClear) {
XValue ret = mReturnVal;
if( bClear )
mReturnVal = null ;
return ret;
}
public ClassLoader getClassLoader(XValue classpath) throws CoreException
{
// No class path sent, use this shells or this class
if( classpath == null ){
if( mClassLoader != null )
return mClassLoader ;
else
return this.getClass().getClassLoader();
}
final List<URL> urls = new ArrayList<URL>();
for( XdmItem item : classpath.asXdmValue() ){
String cp = item.getStringValue();
URL url = getURL(cp);
urls.add(url);
}
final ClassLoader parent = getClass().getClassLoader();
URLClassLoader loader = AccessController.doPrivileged(new PrivilegedAction<URLClassLoader>() {
public URLClassLoader run() {
return new URLClassLoader( (URL[]) urls.toArray(new URL[urls.size()]), parent );
}
});
return loader;
}
public void printLoc(Logger logger, SourceLocation loc) {
if( loc != null ){
String sLoc = loc.toString();
logger.info( sLoc );
printErr(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 )
{
if( mTraps == null )
return;
String scmd = mTraps.get(signal);
if( scmd == null )
return ;
try {
Command 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 synchronized Shell getParent()
{
return mParent;
}
}
//
//
//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.
//