--- /dev/null 2014-06-09 15:34:15.646999981 +0100 +++ new/src/java.scripting/share/classes/com/sun/tools/script/shell/Main.java 2014-11-20 16:04:12.768819113 +0000 @@ -0,0 +1,586 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.tools.script.shell; + +import java.io.*; +import java.net.*; +import java.text.*; +import java.util.*; +import javax.script.*; + +/** + * This is the main class for Java script shell. + */ +public class Main { + /** + * main entry point to the command line tool + * @param args command line argument array + */ + public static void main(String[] args) { + // parse command line options + String[] scriptArgs = processOptions(args); + + // process each script command + for (Command cmd : scripts) { + cmd.run(scriptArgs); + } + + System.exit(EXIT_SUCCESS); + } + + // Each -e or -f or interactive mode is represented + // by an instance of Command. + private static interface Command { + public void run(String[] arguments); + } + + /** + * Parses and processes command line options. + * @param args command line argument array + */ + private static String[] processOptions(String[] args) { + // current scripting language selected + String currentLanguage = DEFAULT_LANGUAGE; + // current script file encoding selected + String currentEncoding = null; + + // check for -classpath or -cp first + checkClassPath(args); + + // have we seen -e or -f ? + boolean seenScript = false; + // have we seen -f - already? + boolean seenStdin = false; + for (int i=0; i < args.length; i++) { + String arg = args[i]; + if (arg.equals("-classpath") || + arg.equals("-cp")) { + // handled already, just continue + i++; + continue; + } + + // collect non-option arguments and pass these as script arguments + if (!arg.startsWith("-")) { + int numScriptArgs; + int startScriptArg; + if (seenScript) { + // if we have seen -e or -f already all non-option arguments + // are passed as script arguments + numScriptArgs = args.length - i; + startScriptArg = i; + } else { + // if we have not seen -e or -f, first non-option argument + // is treated as script file name and rest of the non-option + // arguments are passed to script as script arguments + numScriptArgs = args.length - i - 1; + startScriptArg = i + 1; + ScriptEngine se = getScriptEngine(currentLanguage); + addFileSource(se, args[i], currentEncoding); + } + // collect script arguments and return to main + String[] result = new String[numScriptArgs]; + System.arraycopy(args, startScriptArg, result, 0, numScriptArgs); + return result; + } + + if (arg.startsWith("-D")) { + String value = arg.substring(2); + int eq = value.indexOf('='); + if (eq != -1) { + System.setProperty(value.substring(0, eq), + value.substring(eq + 1)); + } else { + if (!value.equals("")) { + System.setProperty(value, ""); + } else { + // do not allow empty property name + usage(EXIT_CMD_NO_PROPNAME); + } + } + continue; + } else if (arg.equals("-?") || arg.equals("-help")) { + usage(EXIT_SUCCESS); + } else if (arg.equals("-e")) { + seenScript = true; + if (++i == args.length) + usage(EXIT_CMD_NO_SCRIPT); + + ScriptEngine se = getScriptEngine(currentLanguage); + addStringSource(se, args[i]); + continue; + } else if (arg.equals("-encoding")) { + if (++i == args.length) + usage(EXIT_CMD_NO_ENCODING); + currentEncoding = args[i]; + continue; + } else if (arg.equals("-f")) { + seenScript = true; + if (++i == args.length) + usage(EXIT_CMD_NO_FILE); + ScriptEngine se = getScriptEngine(currentLanguage); + if (args[i].equals("-")) { + if (seenStdin) { + usage(EXIT_MULTIPLE_STDIN); + } else { + seenStdin = true; + } + addInteractiveMode(se); + } else { + addFileSource(se, args[i], currentEncoding); + } + continue; + } else if (arg.equals("-l")) { + if (++i == args.length) + usage(EXIT_CMD_NO_LANG); + currentLanguage = args[i]; + continue; + } else if (arg.equals("-q")) { + listScriptEngines(); + } + // some unknown option... + usage(EXIT_UNKNOWN_OPTION); + } + + if (! seenScript) { + ScriptEngine se = getScriptEngine(currentLanguage); + addInteractiveMode(se); + } + return new String[0]; + } + + /** + * Adds interactive mode Command + * @param se ScriptEngine to use in interactive mode. + */ + private static void addInteractiveMode(final ScriptEngine se) { + scripts.add(new Command() { + public void run(String[] args) { + setScriptArguments(se, args); + processSource(se, "-", null); + } + }); + } + + /** + * Adds script source file Command + * @param se ScriptEngine used to evaluate the script file + * @param fileName script file name + * @param encoding script file encoding + */ + private static void addFileSource(final ScriptEngine se, + final String fileName, + final String encoding) { + scripts.add(new Command() { + public void run(String[] args) { + setScriptArguments(se, args); + processSource(se, fileName, encoding); + } + }); + } + + /** + * Adds script string source Command + * @param se ScriptEngine to be used to evaluate the script string + * @param source Script source string + */ + private static void addStringSource(final ScriptEngine se, + final String source) { + scripts.add(new Command() { + public void run(String[] args) { + setScriptArguments(se, args); + String oldFile = setScriptFilename(se, ""); + try { + evaluateString(se, source); + } finally { + setScriptFilename(se, oldFile); + } + } + }); + } + + /** + * Prints list of script engines available and exits. + */ + private static void listScriptEngines() { + List factories = engineManager.getEngineFactories(); + for (ScriptEngineFactory factory: factories) { + getError().println(getMessage("engine.info", + new Object[] { factory.getLanguageName(), + factory.getLanguageVersion(), + factory.getEngineName(), + factory.getEngineVersion() + })); + } + System.exit(EXIT_SUCCESS); + } + + /** + * Processes a given source file or standard input. + * @param se ScriptEngine to be used to evaluate + * @param filename file name, can be null + * @param encoding script file encoding, can be null + */ + private static void processSource(ScriptEngine se, String filename, + String encoding) { + if (filename.equals("-")) { + BufferedReader in = new BufferedReader + (new InputStreamReader(getIn())); + boolean hitEOF = false; + String prompt = getPrompt(se); + se.put(ScriptEngine.FILENAME, ""); + while (!hitEOF) { + getError().print(prompt); + String source = ""; + try { + source = in.readLine(); + } catch (IOException ioe) { + getError().println(ioe.toString()); + } + if (source == null) { + hitEOF = true; + break; + } + Object res = evaluateString(se, source, false); + if (res != null) { + res = res.toString(); + if (res == null) { + res = "null"; + } + getError().println(res); + } + } + } else { + FileInputStream fis = null; + try { + fis = new FileInputStream(filename); + } catch (FileNotFoundException fnfe) { + getError().println(getMessage("file.not.found", + new Object[] { filename })); + System.exit(EXIT_FILE_NOT_FOUND); + } + evaluateStream(se, fis, filename, encoding); + } + } + + /** + * Evaluates given script source + * @param se ScriptEngine to evaluate the string + * @param script Script source string + * @param exitOnError whether to exit the process on script error + */ + private static Object evaluateString(ScriptEngine se, + String script, boolean exitOnError) { + try { + return se.eval(script); + } catch (ScriptException sexp) { + getError().println(getMessage("string.script.error", + new Object[] { sexp.getMessage() })); + if (exitOnError) + System.exit(EXIT_SCRIPT_ERROR); + } catch (Exception exp) { + exp.printStackTrace(getError()); + if (exitOnError) + System.exit(EXIT_SCRIPT_ERROR); + } + + return null; + } + + /** + * Evaluate script string source and exit on script error + * @param se ScriptEngine to evaluate the string + * @param script Script source string + */ + private static void evaluateString(ScriptEngine se, String script) { + evaluateString(se, script, true); + } + + /** + * Evaluates script from given reader + * @param se ScriptEngine to evaluate the string + * @param reader Reader from which is script is read + * @param name file name to report in error. + */ + private static Object evaluateReader(ScriptEngine se, + Reader reader, String name) { + String oldFilename = setScriptFilename(se, name); + try { + return se.eval(reader); + } catch (ScriptException sexp) { + getError().println(getMessage("file.script.error", + new Object[] { name, sexp.getMessage() })); + System.exit(EXIT_SCRIPT_ERROR); + } catch (Exception exp) { + exp.printStackTrace(getError()); + System.exit(EXIT_SCRIPT_ERROR); + } finally { + setScriptFilename(se, oldFilename); + } + return null; + } + + /** + * Evaluates given input stream + * @param se ScriptEngine to evaluate the string + * @param is InputStream from which script is read + * @param name file name to report in error + */ + private static Object evaluateStream(ScriptEngine se, + InputStream is, String name, + String encoding) { + BufferedReader reader = null; + if (encoding != null) { + try { + reader = new BufferedReader(new InputStreamReader(is, + encoding)); + } catch (UnsupportedEncodingException uee) { + getError().println(getMessage("encoding.unsupported", + new Object[] { encoding })); + System.exit(EXIT_NO_ENCODING_FOUND); + } + } else { + reader = new BufferedReader(new InputStreamReader(is)); + } + return evaluateReader(se, reader, name); + } + + /** + * Prints usage message and exits + * @param exitCode process exit code + */ + private static void usage(int exitCode) { + getError().println(getMessage("main.usage", + new Object[] { PROGRAM_NAME })); + System.exit(exitCode); + } + + /** + * Gets prompt for interactive mode + * @return prompt string to use + */ + private static String getPrompt(ScriptEngine se) { + List names = se.getFactory().getNames(); + return names.get(0) + "> "; + } + + /** + * Get formatted, localized error message + */ + private static String getMessage(String key, Object[] params) { + return MessageFormat.format(msgRes.getString(key), params); + } + + // input stream from where we will read + private static InputStream getIn() { + return System.in; + } + + // stream to print error messages + private static PrintStream getError() { + return System.err; + } + + // get current script engine + private static ScriptEngine getScriptEngine(String lang) { + ScriptEngine se = engines.get(lang); + if (se == null) { + se = engineManager.getEngineByName(lang); + if (se == null) { + getError().println(getMessage("engine.not.found", + new Object[] { lang })); + System.exit(EXIT_ENGINE_NOT_FOUND); + } + + // initialize the engine + initScriptEngine(se); + // to avoid re-initialization of engine, store it in a map + engines.put(lang, se); + } + return se; + } + + // initialize a given script engine + private static void initScriptEngine(ScriptEngine se) { + // put engine global variable + se.put("engine", se); + + // load init. file from resource + List exts = se.getFactory().getExtensions(); + InputStream sysIn = null; + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + for (String ext : exts) { + sysIn = cl.getResourceAsStream("com/sun/tools/script/shell/init." + + ext); + if (sysIn != null) break; + } + if (sysIn != null) { + evaluateStream(se, sysIn, "", null); + } + } + + /** + * Checks for -classpath, -cp in command line args. Creates a ClassLoader + * and sets it as Thread context loader for current thread. + * + * @param args command line argument array + */ + private static void checkClassPath(String[] args) { + String classPath = null; + for (int i = 0; i < args.length; i++) { + if (args[i].equals("-classpath") || + args[i].equals("-cp")) { + if (++i == args.length) { + // just -classpath or -cp with no value + usage(EXIT_CMD_NO_CLASSPATH); + } else { + classPath = args[i]; + } + } + } + + if (classPath != null) { + /* We create a class loader, configure it with specified + * classpath values and set the same as context loader. + * Note that ScriptEngineManager uses context loader to + * load script engines. So, this ensures that user defined + * script engines will be loaded. For classes referred + * from scripts, Rhino engine uses thread context loader + * but this is script engine dependent. We don't have + * script engine independent solution anyway. Unless we + * know the class loader used by a specific engine, we + * can't configure correct loader. + */ + URL[] urls = pathToURLs(classPath); + URLClassLoader loader = new URLClassLoader(urls); + Thread.currentThread().setContextClassLoader(loader); + } + + // now initialize script engine manager. Note that this has to + // be done after setting the context loader so that manager + // will see script engines from user specified classpath + engineManager = new ScriptEngineManager(); + } + + /** + * Utility method for converting a search path string to an array + * of directory and JAR file URLs. + * + * @param path the search path string + * @return the resulting array of directory and JAR file URLs + */ + private static URL[] pathToURLs(String path) { + String[] components = path.split(File.pathSeparator); + URL[] urls = new URL[components.length]; + int count = 0; + while(count < components.length) { + URL url = fileToURL(new File(components[count])); + if (url != null) { + urls[count++] = url; + } + } + if (urls.length != count) { + URL[] tmp = new URL[count]; + System.arraycopy(urls, 0, tmp, 0, count); + urls = tmp; + } + return urls; + } + + /** + * Returns the directory or JAR file URL corresponding to the specified + * local file name. + * + * @param file the File object + * @return the resulting directory or JAR file URL, or null if unknown + */ + private static URL fileToURL(File file) { + String name; + try { + name = file.getCanonicalPath(); + } catch (IOException e) { + name = file.getAbsolutePath(); + } + name = name.replace(File.separatorChar, '/'); + if (!name.startsWith("/")) { + name = "/" + name; + } + // If the file does not exist, then assume that it's a directory + if (!file.isFile()) { + name = name + "/"; + } + try { + return new URL("file", "", name); + } catch (MalformedURLException e) { + throw new IllegalArgumentException("file"); + } + } + + private static void setScriptArguments(ScriptEngine se, String[] args) { + se.put("arguments", args); + se.put(ScriptEngine.ARGV, args); + } + + private static String setScriptFilename(ScriptEngine se, String name) { + String oldName = (String) se.get(ScriptEngine.FILENAME); + se.put(ScriptEngine.FILENAME, name); + return oldName; + } + + // exit codes + private static final int EXIT_SUCCESS = 0; + private static final int EXIT_CMD_NO_CLASSPATH = 1; + private static final int EXIT_CMD_NO_FILE = 2; + private static final int EXIT_CMD_NO_SCRIPT = 3; + private static final int EXIT_CMD_NO_LANG = 4; + private static final int EXIT_CMD_NO_ENCODING = 5; + private static final int EXIT_CMD_NO_PROPNAME = 6; + private static final int EXIT_UNKNOWN_OPTION = 7; + private static final int EXIT_ENGINE_NOT_FOUND = 8; + private static final int EXIT_NO_ENCODING_FOUND = 9; + private static final int EXIT_SCRIPT_ERROR = 10; + private static final int EXIT_FILE_NOT_FOUND = 11; + private static final int EXIT_MULTIPLE_STDIN = 12; + + // default scripting language + private static final String DEFAULT_LANGUAGE = "js"; + // list of scripts to process + private static List scripts; + // the script engine manager + private static ScriptEngineManager engineManager; + // map of engines we loaded + private static Map engines; + // error messages resource + private static ResourceBundle msgRes; + private static String BUNDLE_NAME = "com.sun.tools.script.shell.messages"; + private static String PROGRAM_NAME = "jrunscript"; + + static { + scripts = new ArrayList(); + engines = new HashMap(); + msgRes = ResourceBundle.getBundle(BUNDLE_NAME, Locale.getDefault()); + } +}