--- old/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CommandExecutor.java 2020-04-15 18:49:42.000000000 +0530 +++ /dev/null 2020-04-15 18:49:42.000000000 +0530 @@ -1,898 +0,0 @@ -/* - * Copyright (c) 2016, 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 jdk.nashorn.internal.runtime; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.StreamTokenizer; -import java.io.StringReader; -import java.lang.ProcessBuilder.Redirect; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import static jdk.nashorn.internal.runtime.CommandExecutor.RedirectType.*; -import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError; - -/** - * The CommandExecutor class provides support for Nashorn's $EXEC - * builtin function. CommandExecutor provides support for command parsing, - * I/O redirection, piping, completion timeouts, # comments, and simple - * environment variable management (cd, setenv, and unsetenv). - */ -class CommandExecutor { - // Size of byte buffers used for piping. - private static final int BUFFER_SIZE = 1024; - - // Test to see if running on Windows. - private static final boolean IS_WINDOWS = - AccessController.doPrivileged((PrivilegedAction)() -> { - return System.getProperty("os.name").contains("Windows"); - }); - - // Cygwin drive alias prefix. - private static final String CYGDRIVE = "/cygdrive/"; - - // User's home directory - private static final String HOME_DIRECTORY = - AccessController.doPrivileged((PrivilegedAction)() -> { - return System.getProperty("user.home"); - }); - - // Various types of standard redirects. - enum RedirectType { - NO_REDIRECT, - REDIRECT_INPUT, - REDIRECT_OUTPUT, - REDIRECT_OUTPUT_APPEND, - REDIRECT_ERROR, - REDIRECT_ERROR_APPEND, - REDIRECT_OUTPUT_ERROR_APPEND, - REDIRECT_ERROR_TO_OUTPUT - }; - - // Prefix strings to standard redirects. - private static final String[] redirectPrefixes = new String[] { - "<", - "0<", - ">", - "1>", - ">>", - "1>>", - "2>", - "2>>", - "&>", - "2>&1" - }; - - // Map from redirectPrefixes to RedirectType. - private static final RedirectType[] redirects = new RedirectType[] { - REDIRECT_INPUT, - REDIRECT_INPUT, - REDIRECT_OUTPUT, - REDIRECT_OUTPUT, - REDIRECT_OUTPUT_APPEND, - REDIRECT_OUTPUT_APPEND, - REDIRECT_ERROR, - REDIRECT_ERROR_APPEND, - REDIRECT_OUTPUT_ERROR_APPEND, - REDIRECT_ERROR_TO_OUTPUT - }; - - /** - * The RedirectInfo class handles checking the next token in a command - * to see if it contains a redirect. If the redirect file does not butt - * against the prefix, then the next token is consumed. - */ - private static class RedirectInfo { - // true if a redirect was encountered on the current command. - private boolean hasRedirects; - // Redirect.PIPE or an input redirect from the command line. - private Redirect inputRedirect; - // Redirect.PIPE or an output redirect from the command line. - private Redirect outputRedirect; - // Redirect.PIPE or an error redirect from the command line. - private Redirect errorRedirect; - // true if the error stream should be merged with output. - private boolean mergeError; - - RedirectInfo() { - this.hasRedirects = false; - this.inputRedirect = Redirect.PIPE; - this.outputRedirect = Redirect.PIPE; - this.errorRedirect = Redirect.PIPE; - this.mergeError = false; - } - - /** - * check - tests to see if the current token contains a redirect - * @param token current command line token - * @param iterator current command line iterator - * @param cwd current working directory - * @return true if token is consumed - */ - boolean check(String token, final Iterator iterator, final String cwd) { - // Iterate through redirect prefixes to file a match. - for (int i = 0; i < redirectPrefixes.length; i++) { - final String prefix = redirectPrefixes[i]; - - // If a match is found. - if (token.startsWith(prefix)) { - // Indicate we have at least one redirect (efficiency.) - hasRedirects = true; - // Map prefix to RedirectType. - final RedirectType redirect = redirects[i]; - // Strip prefix from token - token = token.substring(prefix.length()); - - // Get file from either current or next token. - File file = null; - if (redirect != REDIRECT_ERROR_TO_OUTPUT) { - // Nothing left of current token. - if (token.length() == 0) { - if (iterator.hasNext()) { - // Use next token. - token = iterator.next(); - } else { - // Send to null device if not provided. - token = IS_WINDOWS ? "NUL:" : "/dev/null"; - } - } - - // Redirect file. - file = resolvePath(cwd, token).toFile(); - } - - // Define redirect based on prefix. - switch (redirect) { - case REDIRECT_INPUT: - inputRedirect = Redirect.from(file); - break; - case REDIRECT_OUTPUT: - outputRedirect = Redirect.to(file); - break; - case REDIRECT_OUTPUT_APPEND: - outputRedirect = Redirect.appendTo(file); - break; - case REDIRECT_ERROR: - errorRedirect = Redirect.to(file); - break; - case REDIRECT_ERROR_APPEND: - errorRedirect = Redirect.appendTo(file); - break; - case REDIRECT_OUTPUT_ERROR_APPEND: - outputRedirect = Redirect.to(file); - errorRedirect = Redirect.to(file); - mergeError = true; - break; - case REDIRECT_ERROR_TO_OUTPUT: - mergeError = true; - break; - default: - return false; - } - - // Indicate token is consumed. - return true; - } - } - - // No redirect found. - return false; - } - - /** - * apply - apply the redirects to the current ProcessBuilder. - * @param pb current ProcessBuilder - */ - void apply(final ProcessBuilder pb) { - // Only if there was redirects (saves new structure in ProcessBuilder.) - if (hasRedirects) { - // If output and error are the same file then merge. - final File outputFile = outputRedirect.file(); - final File errorFile = errorRedirect.file(); - - if (outputFile != null && outputFile.equals(errorFile)) { - mergeError = true; - } - - // Apply redirects. - pb.redirectInput(inputRedirect); - pb.redirectOutput(outputRedirect); - pb.redirectError(errorRedirect); - pb.redirectErrorStream(mergeError); - } - } - } - - /** - * The Piper class is responsible for copying from an InputStream to an - * OutputStream without blocking the current thread. - */ - private static class Piper implements java.lang.Runnable { - // Stream to copy from. - private final InputStream input; - // Stream to copy to. - private final OutputStream output; - - private final Thread thread; - - Piper(final InputStream input, final OutputStream output) { - this.input = input; - this.output = output; - this.thread = new Thread(this, "$EXEC Piper"); - } - - /** - * start - start the Piper in a new daemon thread - * @return this Piper - */ - Piper start() { - thread.setDaemon(true); - thread.start(); - return this; - } - - /** - * run - thread action - */ - @Override - public void run() { - try { - // Buffer for copying. - final byte[] b = new byte[BUFFER_SIZE]; - // Read from the InputStream until EOF. - int read; - while (-1 < (read = input.read(b, 0, b.length))) { - // Write available date to OutputStream. - output.write(b, 0, read); - } - } catch (final Exception e) { - // Assume the worst. - throw new RuntimeException("Broken pipe", e); - } finally { - // Make sure the streams are closed. - try { - input.close(); - } catch (final IOException e) { - // Don't care. - } - try { - output.close(); - } catch (final IOException e) { - // Don't care. - } - } - } - - public void join() throws InterruptedException { - thread.join(); - } - - // Exit thread. - } - - // Process exit statuses. - static final int EXIT_SUCCESS = 0; - static final int EXIT_FAILURE = 1; - - // Copy of environment variables used by all processes. - private Map environment; - // Input string if provided on CommandExecutor call. - private String inputString; - // Output string if required from CommandExecutor call. - private String outputString; - // Error string if required from CommandExecutor call. - private String errorString; - // Last process exit code. - private int exitCode; - - // Input stream if provided on CommandExecutor call. - private InputStream inputStream; - // Output stream if provided on CommandExecutor call. - private OutputStream outputStream; - // Error stream if provided on CommandExecutor call. - private OutputStream errorStream; - - // Ordered collection of current or piped ProcessBuilders. - private List processBuilders = new ArrayList<>(); - - CommandExecutor() { - this.environment = new HashMap<>(); - this.inputString = ""; - this.outputString = ""; - this.errorString = ""; - this.exitCode = EXIT_SUCCESS; - this.inputStream = null; - this.outputStream = null; - this.errorStream = null; - this.processBuilders = new ArrayList<>(); - } - - /** - * envVarValue - return the value of the environment variable key, or - * deflt if not found. - * @param key name of environment variable - * @param deflt value to return if not found - * @return value of the environment variable - */ - private String envVarValue(final String key, final String deflt) { - return environment.getOrDefault(key, deflt); - } - - /** - * envVarLongValue - return the value of the environment variable key as a - * long value. - * @param key name of environment variable - * @return long value of the environment variable - */ - private long envVarLongValue(final String key) { - try { - return Long.parseLong(envVarValue(key, "0")); - } catch (final NumberFormatException ex) { - return 0L; - } - } - - /** - * envVarBooleanValue - return the value of the environment variable key as a - * boolean value. true if the value was non-zero, false otherwise. - * @param key name of environment variable - * @return boolean value of the environment variable - */ - private boolean envVarBooleanValue(final String key) { - return envVarLongValue(key) != 0; - } - - /** - * stripQuotes - strip quotes from token if present. Quoted tokens kept - * quotes to prevent search for redirects. - * @param token token to strip - * @return stripped token - */ - private static String stripQuotes(String token) { - if ((token.startsWith("\"") && token.endsWith("\"")) || - token.startsWith("\'") && token.endsWith("\'")) { - token = token.substring(1, token.length() - 1); - } - return token; - } - - /** - * resolvePath - resolves a path against a current working directory. - * @param cwd current working directory - * @param fileName name of file or directory - * @return resolved Path to file - */ - private static Path resolvePath(final String cwd, final String fileName) { - return Paths.get(sanitizePath(cwd)).resolve(fileName).normalize(); - } - - /** - * builtIn - checks to see if the command is a builtin and performs - * appropriate action. - * @param cmd current command - * @param cwd current working directory - * @return true if was a builtin command - */ - private boolean builtIn(final List cmd, final String cwd) { - switch (cmd.get(0)) { - // Set current working directory. - case "cd": - final boolean cygpath = IS_WINDOWS && cwd.startsWith(CYGDRIVE); - // If zero args then use home directory as cwd else use first arg. - final String newCWD = cmd.size() < 2 ? HOME_DIRECTORY : cmd.get(1); - // Normalize the cwd - final Path cwdPath = resolvePath(cwd, newCWD); - - // Check if is a directory. - final File file = cwdPath.toFile(); - if (!file.exists()) { - reportError("file.not.exist", file.toString()); - return true; - } else if (!file.isDirectory()) { - reportError("not.directory", file.toString()); - return true; - } - - // Set PWD environment variable to be picked up as cwd. - // Make sure Cygwin paths look like Unix paths. - String scwd = cwdPath.toString(); - if (cygpath && scwd.length() >= 2 && - Character.isLetter(scwd.charAt(0)) && scwd.charAt(1) == ':') { - scwd = CYGDRIVE + Character.toLowerCase(scwd.charAt(0)) + "/" + scwd.substring(2); - } - environment.put("PWD", scwd); - return true; - - // Set an environment variable. - case "setenv": - if (3 <= cmd.size()) { - final String key = cmd.get(1); - final String value = cmd.get(2); - environment.put(key, value); - } - - return true; - - // Unset an environment variable. - case "unsetenv": - if (2 <= cmd.size()) { - final String key = cmd.get(1); - environment.remove(key); - } - - return true; - } - - return false; - } - - /** - * preprocessCommand - scan the command for redirects, and sanitize the - * executable path - * @param tokens command tokens - * @param cwd current working directory - * @param redirectInfo redirection information - * @return tokens remaining for actual command - */ - private List preprocessCommand(final List tokens, - final String cwd, final RedirectInfo redirectInfo) { - // Tokens remaining for actual command. - final List command = new ArrayList<>(); - - // iterate through all tokens. - final Iterator iterator = tokens.iterator(); - while (iterator.hasNext()) { - final String token = iterator.next(); - - // Check if is a redirect. - if (redirectInfo.check(token, iterator, cwd)) { - // Don't add to the command. - continue; - } - - // Strip quotes and add to command. - command.add(stripQuotes(token)); - } - - if (command.size() > 0) { - command.set(0, sanitizePath(command.get(0))); - } - - return command; - } - - /** - * Sanitize a path in case the underlying platform is Cygwin. In that case, - * convert from the {@code /cygdrive/x} drive specification to the usual - * Windows {@code X:} format. - * - * @param d a String representing a path - * @return a String representing the same path in a form that can be - * processed by the underlying platform - */ - private static String sanitizePath(final String d) { - if (!IS_WINDOWS || (IS_WINDOWS && !d.startsWith(CYGDRIVE))) { - return d; - } - final String pd = d.substring(CYGDRIVE.length()); - if (pd.length() >= 2 && pd.charAt(1) == '/') { - // drive letter plus / -> convert /cygdrive/x/... to X:/... - return pd.charAt(0) + ":" + pd.substring(1); - } else if (pd.length() == 1) { - // just drive letter -> convert /cygdrive/x to X: - return pd.charAt(0) + ":"; - } - // remaining case: /cygdrive/ -> can't convert - return d; - } - - /** - * createProcessBuilder - create a ProcessBuilder for the command. - * @param command command tokens - * @param cwd current working directory - * @param redirectInfo redirect information - */ - private void createProcessBuilder(final List command, - final String cwd, final RedirectInfo redirectInfo) { - // Create new ProcessBuilder. - final ProcessBuilder pb = new ProcessBuilder(command); - // Set current working directory. - pb.directory(new File(sanitizePath(cwd))); - - // Map environment variables. - final Map processEnvironment = pb.environment(); - processEnvironment.clear(); - processEnvironment.putAll(environment); - - // Apply redirects. - redirectInfo.apply(pb); - // Add to current list of commands. - processBuilders.add(pb); - } - - /** - * command - process the command - * @param tokens tokens of the command - * @param isPiped true if the output of this command should be piped to the next - */ - private void command(final List tokens, final boolean isPiped) { - // Test to see if we should echo the command to output. - if (envVarBooleanValue("JJS_ECHO")) { - System.out.println(String.join(" ", tokens)); - } - - // Get the current working directory. - final String cwd = envVarValue("PWD", HOME_DIRECTORY); - // Preprocess the command for redirects. - final RedirectInfo redirectInfo = new RedirectInfo(); - final List command = preprocessCommand(tokens, cwd, redirectInfo); - - // Skip if empty or a built in. - if (command.isEmpty() || builtIn(command, cwd)) { - return; - } - - // Create ProcessBuilder with cwd and redirects set. - createProcessBuilder(command, cwd, redirectInfo); - - // If piped, wait for the next command. - if (isPiped) { - return; - } - - // Fetch first and last ProcessBuilder. - final ProcessBuilder firstProcessBuilder = processBuilders.get(0); - final ProcessBuilder lastProcessBuilder = processBuilders.get(processBuilders.size() - 1); - - // Determine which streams have not be redirected from pipes. - boolean inputIsPipe = firstProcessBuilder.redirectInput() == Redirect.PIPE; - boolean outputIsPipe = lastProcessBuilder.redirectOutput() == Redirect.PIPE; - boolean errorIsPipe = lastProcessBuilder.redirectError() == Redirect.PIPE; - final boolean inheritIO = envVarBooleanValue("JJS_INHERIT_IO"); - - // If not redirected and inputStream is current processes' input. - if (inputIsPipe && (inheritIO || inputStream == System.in)) { - // Inherit current processes' input. - firstProcessBuilder.redirectInput(Redirect.INHERIT); - inputIsPipe = false; - } - - // If not redirected and outputStream is current processes' output. - if (outputIsPipe && (inheritIO || outputStream == System.out)) { - // Inherit current processes' output. - lastProcessBuilder.redirectOutput(Redirect.INHERIT); - outputIsPipe = false; - } - - // If not redirected and errorStream is current processes' error. - if (errorIsPipe && (inheritIO || errorStream == System.err)) { - // Inherit current processes' error. - lastProcessBuilder.redirectError(Redirect.INHERIT); - errorIsPipe = false; - } - - // Start the processes. - final List processes = new ArrayList<>(); - for (final ProcessBuilder pb : processBuilders) { - try { - processes.add(pb.start()); - } catch (final IOException ex) { - reportError("unknown.command", String.join(" ", pb.command())); - return; - } - } - - // Clear processBuilders for next command. - processBuilders.clear(); - - // Get first and last process. - final Process firstProcess = processes.get(0); - final Process lastProcess = processes.get(processes.size() - 1); - - // Prepare for string based i/o if no redirection or provided streams. - ByteArrayOutputStream byteOutputStream = null; - ByteArrayOutputStream byteErrorStream = null; - - final List piperThreads = new ArrayList<>(); - - // If input is not redirected. - if (inputIsPipe) { - // If inputStream other than System.in is provided. - if (inputStream != null) { - // Pipe inputStream to first process output stream. - piperThreads.add(new Piper(inputStream, firstProcess.getOutputStream()).start()); - } else { - // Otherwise assume an input string has been provided. - piperThreads.add(new Piper(new ByteArrayInputStream(inputString.getBytes()), firstProcess.getOutputStream()).start()); - } - } - - // If output is not redirected. - if (outputIsPipe) { - // If outputStream other than System.out is provided. - if (outputStream != null ) { - // Pipe outputStream from last process input stream. - piperThreads.add(new Piper(lastProcess.getInputStream(), outputStream).start()); - } else { - // Otherwise assume an output string needs to be prepared. - byteOutputStream = new ByteArrayOutputStream(BUFFER_SIZE); - piperThreads.add(new Piper(lastProcess.getInputStream(), byteOutputStream).start()); - } - } - - // If error is not redirected. - if (errorIsPipe) { - // If errorStream other than System.err is provided. - if (errorStream != null) { - piperThreads.add(new Piper(lastProcess.getErrorStream(), errorStream).start()); - } else { - // Otherwise assume an error string needs to be prepared. - byteErrorStream = new ByteArrayOutputStream(BUFFER_SIZE); - piperThreads.add(new Piper(lastProcess.getErrorStream(), byteErrorStream).start()); - } - } - - // Pipe commands in between. - for (int i = 0, n = processes.size() - 1; i < n; i++) { - final Process prev = processes.get(i); - final Process next = processes.get(i + 1); - piperThreads.add(new Piper(prev.getInputStream(), next.getOutputStream()).start()); - } - - // Wind up processes. - try { - // Get the user specified timeout. - final long timeout = envVarLongValue("JJS_TIMEOUT"); - - // If user specified timeout (milliseconds.) - if (timeout != 0) { - // Wait for last process, with timeout. - if (lastProcess.waitFor(timeout, TimeUnit.MILLISECONDS)) { - // Get exit code of last process. - exitCode = lastProcess.exitValue(); - } else { - reportError("timeout", Long.toString(timeout)); - } - } else { - // Wait for last process and get exit code. - exitCode = lastProcess.waitFor(); - } - // Wait for all piper threads to terminate - for (final Piper piper : piperThreads) { - piper.join(); - } - - // Accumulate the output and error streams. - outputString += byteOutputStream != null ? byteOutputStream.toString() : ""; - errorString += byteErrorStream != null ? byteErrorStream.toString() : ""; - } catch (final InterruptedException ex) { - // Kill any living processes. - processes.stream().forEach(p -> { - if (p.isAlive()) { - p.destroy(); - } - - // Get the first error code. - exitCode = exitCode == 0 ? p.exitValue() : exitCode; - }); - } - - // If we got a non-zero exit code then possibly throw an exception. - if (exitCode != 0 && envVarBooleanValue("JJS_THROW_ON_EXIT")) { - throw rangeError("exec.returned.non.zero", ScriptRuntime.safeToString(exitCode)); - } - } - - /** - * createTokenizer - build up StreamTokenizer for the command script - * @param script command script to parsed - * @return StreamTokenizer for command script - */ - private static StreamTokenizer createTokenizer(final String script) { - final StreamTokenizer tokenizer = new StreamTokenizer(new StringReader(script)); - tokenizer.resetSyntax(); - // Default all characters to word. - tokenizer.wordChars(0, 255); - // Spaces and special characters are white spaces. - tokenizer.whitespaceChars(0, ' '); - // Ignore # comments. - tokenizer.commentChar('#'); - // Handle double and single quote strings. - tokenizer.quoteChar('"'); - tokenizer.quoteChar('\''); - // Need to recognize the end of a command. - tokenizer.eolIsSignificant(true); - // Command separator. - tokenizer.ordinaryChar(';'); - // Pipe separator. - tokenizer.ordinaryChar('|'); - - return tokenizer; - } - - /** - * process - process a command string - * @param script command script to parsed - */ - void process(final String script) { - // Build up StreamTokenizer for the command script. - final StreamTokenizer tokenizer = createTokenizer(script); - - // Prepare to accumulate command tokens. - final List command = new ArrayList<>(); - // Prepare to acumulate partial tokens joined with "\ ". - final StringBuilder sb = new StringBuilder(); - - try { - // Fetch next token until end of script. - while (tokenizer.nextToken() != StreamTokenizer.TT_EOF) { - // Next word token. - String token = tokenizer.sval; - - // If special token. - if (token == null) { - // Flush any partial token. - if (sb.length() != 0) { - command.add(sb.append(token).toString()); - sb.setLength(0); - } - - // Process a completed command. - // Will be either ';' (command end) or '|' (pipe), true if '|'. - command(command, tokenizer.ttype == '|'); - - if (exitCode != EXIT_SUCCESS) { - return; - } - - // Start with a new set of tokens. - command.clear(); - } else if (token.endsWith("\\")) { - // Backslash followed by space. - sb.append(token.substring(0, token.length() - 1)).append(' '); - } else if (sb.length() == 0) { - // If not a word then must be a quoted string. - if (tokenizer.ttype != StreamTokenizer.TT_WORD) { - // Quote string, sb is free to use (empty.) - sb.append((char)tokenizer.ttype); - sb.append(token); - sb.append((char)tokenizer.ttype); - token = sb.toString(); - sb.setLength(0); - } - - command.add(token); - } else { - // Partial token pending. - command.add(sb.append(token).toString()); - sb.setLength(0); - } - } - } catch (final IOException ex) { - // Do nothing. - } - - // Partial token pending. - if (sb.length() != 0) { - command.add(sb.toString()); - } - - // Process last command. - command(command, false); - } - - /** - * process - process a command array of strings - * @param tokens command script to be processed - */ - void process(final List tokens) { - // Prepare to accumulate command tokens. - final List command = new ArrayList<>(); - - // Iterate through tokens. - final Iterator iterator = tokens.iterator(); - while (iterator.hasNext() && exitCode == EXIT_SUCCESS) { - // Next word token. - final String token = iterator.next(); - - if (token == null) { - continue; - } - - switch (token) { - case "|": - // Process as a piped command. - command(command, true); - // Start with a new set of tokens. - command.clear(); - - continue; - case ";": - // Process as a normal command. - command(command, false); - // Start with a new set of tokens. - command.clear(); - - continue; - } - - command.add(token); - } - - // Process last command. - command(command, false); - } - - void reportError(final String msg, final String object) { - errorString += ECMAErrors.getMessage("range.error.exec." + msg, object); - exitCode = EXIT_FAILURE; - } - - String getOutputString() { - return outputString; - } - - String getErrorString() { - return errorString; - } - - int getExitCode() { - return exitCode; - } - - void setEnvironment(final Map environment) { - this.environment = environment; - } - - void setInputStream(final InputStream inputStream) { - this.inputStream = inputStream; - } - - void setInputString(final String inputString) { - this.inputString = inputString; - } - - void setOutputStream(final OutputStream outputStream) { - this.outputStream = outputStream; - } - - void setErrorStream(final OutputStream errorStream) { - this.errorStream = errorStream; - } -}