/** * MVEL 2.0 * Copyright (C) 2007 The Codehaus * Mike Brock, Dhanji Prasanna, John Graham, Mark Proctor * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.mvel2.sh; import org.mvel2.MVELInterpretedRuntime; import org.mvel2.ParserContext; import org.mvel2.integration.VariableResolverFactory; import org.mvel2.integration.impl.MapVariableResolverFactory; import org.mvel2.sh.command.basic.BasicCommandSet; import org.mvel2.sh.command.file.FileCommandSet; import org.mvel2.templates.TemplateRuntime; import org.mvel2.util.StringAppender; import org.mvel2.util.PropertyTools; import java.io.*; import java.util.*; import static java.lang.Boolean.parseBoolean; import static java.lang.Runtime.getRuntime; import static java.lang.System.arraycopy; import static java.lang.System.getProperty; import static java.util.ResourceBundle.getBundle; import static org.mvel2.MVEL.compileExpression; import static org.mvel2.MVEL.executeExpression; import static org.mvel2.util.PropertyTools.contains; /** * A shell session. */ public class ShellSession { public static final String PROMPT_VAR = "$PROMPT"; private static final String[] EMPTY = new String[0]; private final Map<String, Command> commands = new HashMap<String, Command>(); private Map<String, Object> variables; private Map<String, String> env; private Object ctxObject; ParserContext pCtx = new ParserContext(); VariableResolverFactory lvrf; private int depth; private int cdepth; private boolean multi = false; private int multiIndentSize = 0; private PrintStream out = System.out; private String prompt; private String commandBuffer; StringAppender inBuffer = new StringAppender(); final BufferedReader readBuffer = new BufferedReader(new InputStreamReader(System.in)); public ShellSession() { System.out.println("Starting session..."); variables = new HashMap<String, Object>(); env = new HashMap<String, String>(); commands.putAll(new BasicCommandSet().load()); commands.putAll(new FileCommandSet().load()); env.put(PROMPT_VAR, DefaultEnvironment.PROMPT); env.put("$OS_NAME", getProperty("os.name")); env.put("$OS_VERSION", getProperty("os.version")); env.put("$JAVA_VERSION", PropertyTools.getJavaVersion()); env.put("$CWD", new File(".").getAbsolutePath()); env.put("$COMMAND_PASSTRU", "false"); env.put("$PRINTOUTPUT", "true"); env.put("$ECHO", "false"); env.put("$SHOW_TRACES", "true"); env.put("$USE_OPTIMIZER_ALWAYS", "false"); env.put("$PATH", ""); try { ResourceBundle bundle = getBundle(".mvelsh.properties"); Enumeration<String> enumer = bundle.getKeys(); String key; while (enumer.hasMoreElements()) { env.put(key = enumer.nextElement(), bundle.getString(key)); } } catch (MissingResourceException e) { System.out.println("No config file found. Loading default config."); if (!contains(getProperty("os.name").toLowerCase(), "windows")) { env.put("$PATH", "/bin:/usr/bin:/sbin:/usr/sbin"); } } lvrf = new MapVariableResolverFactory(variables, new MapVariableResolverFactory(env)); } public ShellSession(String init) { this(); exec(init); } private void _exec() { String[] inTokens; Object outputBuffer; final PrintStream sysPrintStream = System.out; final PrintStream sysErrorStream = System.err; final InputStream sysInputStream = System.in; File execFile; if ("true".equals(env.get("$ECHO"))) { out.println(">" + commandBuffer); out.flush(); } inTokens = inBuffer.append(commandBuffer).toString().split("\\s"); if (inTokens.length != 0 && commands.containsKey(inTokens[0])) { commandBuffer = null; String[] passParameters; if (inTokens.length > 1) { arraycopy(inTokens, 1, passParameters = new String[inTokens.length - 1], 0, passParameters.length); } else { passParameters = EMPTY; } try { commands.get(inTokens[0]).execute(this, passParameters); } catch (CommandException e) { out.append("Error: ").append(e.getMessage()).append("\n"); } } else { commandBuffer = null; try { if (shouldDefer(inBuffer)) { multi = true; return; } else { multi = false; } if (parseBoolean(env.get("$USE_OPTIMIZER_ALWAYS"))) { outputBuffer = executeExpression(compileExpression(inBuffer.toString()), ctxObject, lvrf); } else { MVELInterpretedRuntime runtime = new MVELInterpretedRuntime(inBuffer.toString(), ctxObject, lvrf, pCtx); outputBuffer = runtime.parse(); } } catch (Exception e) { if ("true".equals(env.get("$COMMAND_PASSTHRU"))) { String[] paths; String s; if ((s = inTokens[0]).startsWith("./")) { s = new File(env.get("$CWD")).getAbsolutePath() + s.substring(s.indexOf('/')); paths = new String[]{s}; } else { paths = env.get("$PATH").split("(:|;)"); } boolean successfulExec = false; for (String execPath : paths) { if ((execFile = new File(execPath + "/" + s)).exists() && execFile.isFile()) { successfulExec = true; String[] execString = new String[inTokens.length]; execString[0] = execFile.getAbsolutePath(); System.arraycopy(inTokens, 1, execString, 1, inTokens.length - 1); try { final Process p = getRuntime().exec(execString); final OutputStream outStream = p.getOutputStream(); final InputStream inStream = p.getInputStream(); final InputStream errStream = p.getErrorStream(); final RunState runState = new RunState(this); final Thread pollingThread = new Thread(new Runnable() { public void run() { byte[] buf = new byte[25]; int read; while (true) { try { while ((read = inStream.read(buf)) > 0) { for (int i = 0; i < read; i++) { sysPrintStream.print((char) buf[i]); } sysPrintStream.flush(); } if (!runState.isRunning()) break; } catch (Exception e) { break; } } sysPrintStream.flush(); if (!multi) { multiIndentSize = (prompt = String.valueOf(TemplateRuntime.eval(env.get("$PROMPT"), variables))).length(); out.append(prompt); } else { out.append(">").append(indent((multiIndentSize - 1) + (depth * 4))); } } }); final Thread watchThread = new Thread(new Runnable() { public void run() { Thread runningThread = new Thread(new Runnable() { public void run() { try { String read; while (runState.isRunning()) { while ((read = readBuffer.readLine()) != null) { if (runState.isRunning()) { for (char c : read.toCharArray()) { outStream.write((byte) c); } } else { runState.getSession().setCommandBuffer(read); break; } } } outStream.write((byte) '\n'); outStream.flush(); } catch (Exception e2) { } } }); runningThread.setPriority(Thread.MIN_PRIORITY); runningThread.start(); try { p.waitFor(); } catch (InterruptedException e) { // nothing; } sysPrintStream.flush(); runState.setRunning(false); try { runningThread.join(); } catch (InterruptedException e) { // nothing;� } } }); pollingThread.setPriority(Thread.MIN_PRIORITY); pollingThread.start(); watchThread.setPriority(Thread.MIN_PRIORITY); watchThread.start(); watchThread.join(); try { pollingThread.notify(); } catch (Exception ne) { } } catch (Exception e2) { // fall through; } } } if (successfulExec) { inBuffer.reset(); return; } } ByteArrayOutputStream stackTraceCap = new ByteArrayOutputStream(); PrintStream capture = new PrintStream(stackTraceCap); e.printStackTrace(capture); capture.flush(); env.put("$LAST_STACK_TRACE", new String(stackTraceCap.toByteArray())); if (parseBoolean(env.get("$SHOW_TRACE"))) { out.println(env.get("$LAST_STACK_TRACE")); } else { out.println(e.toString()); } inBuffer.reset(); return; } if (outputBuffer != null && "true".equals(env.get("$PRINTOUTPUT"))) { if (outputBuffer.getClass().isArray()) { out.println(Arrays.toString((Object[]) outputBuffer)); } else { out.println(String.valueOf(outputBuffer)); } } } inBuffer.reset(); } //todo: fix this public void run() { final BufferedReader readBuffer = new BufferedReader(new InputStreamReader(System.in)); try { //noinspection InfiniteLoopStatement while (true) { printPrompt(); if (commandBuffer == null) { commandBuffer = readBuffer.readLine(); } _exec(); } } catch (Exception e) { e.printStackTrace(); System.out.println("unexpected exception. exiting."); } } public void printPrompt() { if (!multi) { multiIndentSize = (prompt = String.valueOf(TemplateRuntime.eval(env.get("$PROMPT"), variables))).length(); out.append(prompt); } else { out.append(">").append(indent((multiIndentSize - 1) + (depth * 4))); } } public boolean shouldDefer(StringAppender inBuf) { char[] buffer = new char[inBuf.length()]; inBuf.getChars(0, inBuf.length(), buffer, 0); depth = cdepth = 0; for (int i = 0; i < buffer.length; i++) { switch (buffer[i]) { case '/': if (i + 1 < buffer.length && buffer[i + 1] == '*') { cdepth++; } break; case '*': if (i + 1 < buffer.length && buffer[i + 1] == '/') { cdepth--; } break; case '{': depth++; break; case '}': depth--; break; } } return depth + cdepth > 0; } public String indent(int size) { StringBuffer sbuf = new StringBuffer(); for (int i = 0; i < size; i++) sbuf.append(" "); return sbuf.toString(); } public Map<String, Command> getCommands() { return commands; } public Map<String, Object> getVariables() { return variables; } public Map<String, String> getEnv() { return env; } public Object getCtxObject() { return ctxObject; } public void setCtxObject(Object ctxObject) { this.ctxObject = ctxObject; } public String getCommandBuffer() { return commandBuffer; } public void setCommandBuffer(String commandBuffer) { this.commandBuffer = commandBuffer; } public void exec(String command) { for (String c : command.split("\n")) { inBuffer.append(c); _exec(); } } public static final class RunState { private boolean running = true; private ShellSession session; public RunState(ShellSession session) { this.session = session; } public ShellSession getSession() { return session; } public void setSession(ShellSession session) { this.session = session; } public boolean isRunning() { return running; } public void setRunning(boolean running) { this.running = running; } } }