| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.netbeans.modules.nativeexecution; |
| |
| import java.io.File; |
| import java.nio.charset.Charset; |
| import java.text.ParseException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.concurrent.CopyOnWriteArrayList; |
| import javax.swing.event.ChangeListener; |
| import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment; |
| import org.netbeans.modules.nativeexecution.api.NativeProcess; |
| import org.netbeans.modules.nativeexecution.api.pty.Pty; |
| import org.netbeans.modules.nativeexecution.api.util.MacroExpanderFactory; |
| import org.netbeans.modules.nativeexecution.api.util.MacroExpanderFactory.MacroExpander; |
| import org.netbeans.modules.nativeexecution.api.util.MacroMap; |
| import org.netbeans.modules.nativeexecution.api.util.WindowsSupport; |
| import org.openide.util.Utilities; |
| |
| /** |
| * |
| */ |
| // @NotThreadSafe |
| public final class NativeProcessInfo { |
| private static final String DEFAULT_CHARSET = "UTF-8"; // NOI18N |
| |
| public final MacroExpander macroExpander; |
| private final ExecutionEnvironment execEnv; |
| private final boolean isWindows; |
| private final MacroMap environment; |
| private final List<String> arguments = new ArrayList<>(); |
| private final CopyOnWriteArrayList<ChangeListener> listeners = new CopyOnWriteArrayList<>(); |
| private String executable; |
| private String commandLine; |
| private String workingDirectory; |
| private boolean unbuffer; |
| private boolean redirectError; |
| private boolean x11forwarding; |
| private boolean suspend; |
| private Pty pty = null; |
| private boolean runInPty; |
| private boolean expandMacros = true; |
| private Charset charset; |
| private boolean statusEx; |
| |
| public NativeProcessInfo(ExecutionEnvironment execEnv) { |
| this.execEnv = execEnv; |
| this.executable = null; |
| this.unbuffer = false; |
| this.workingDirectory = null; |
| this.macroExpander = MacroExpanderFactory.getExpander(execEnv); |
| this.environment = MacroMap.forExecEnv(execEnv); |
| isWindows = execEnv.isLocal() && Utilities.isWindows(); |
| redirectError = false; |
| } |
| |
| NativeProcessInfo(NativeProcessInfo info, boolean copyListeners) { |
| this.macroExpander = info.macroExpander; |
| this.execEnv = info.execEnv; |
| this.isWindows = info.isWindows; |
| this.environment = info.environment.clone(); |
| this.arguments.addAll(info.arguments); |
| this.executable = info.executable; |
| this.commandLine = info.commandLine; |
| this.workingDirectory = info.workingDirectory; |
| this.unbuffer = info.unbuffer; |
| this.redirectError = info.redirectError; |
| this.x11forwarding = info.x11forwarding; |
| this.suspend = info.suspend; |
| if (copyListeners) { |
| this.listeners.addAll(info.getListenersSnapshot()); |
| } |
| this.pty = info.pty; |
| this.runInPty = info.runInPty; |
| this.expandMacros = info.expandMacros; |
| this.charset = info.charset; |
| this.statusEx = info.statusEx; |
| } |
| |
| public void addChangeListener(ChangeListener listener) { |
| listeners.addIfAbsent(listener); |
| } |
| |
| public void removeChangeListener(ChangeListener listener) { |
| listeners.remove(listener); |
| } |
| |
| public void redirectError(boolean redirectError) { |
| this.redirectError = redirectError; |
| } |
| |
| public void setExecutable(String executable) { |
| this.executable = executable; |
| } |
| |
| @Deprecated |
| public void setCommandLine(String commandLine) { |
| if (isWindows && commandLine != null) { |
| // Until we use java ProcessBuilder on Windows, |
| // we cannot pass a single line to it [IZ#170748] |
| String[] cmdAndArgs = Utilities.parseParameters(commandLine); |
| if (cmdAndArgs.length == 0) { |
| return; |
| } |
| |
| String execFile = cmdAndArgs[0]; |
| setExecutable(execFile); |
| if (cmdAndArgs.length == 1) { |
| return; |
| } |
| |
| List<String> args = new ArrayList<>(cmdAndArgs.length - 1); |
| for (int i = 1; i < cmdAndArgs.length; i++) { |
| args.add(cmdAndArgs[i]); |
| } |
| |
| setArguments(args.toArray(new String[0])); |
| } else { |
| this.commandLine = commandLine; |
| } |
| } |
| |
| public void setWorkingDirectory(String workingDirectory) { |
| this.workingDirectory = workingDirectory; |
| } |
| |
| public void setUnbuffer(boolean unbuffer) { |
| this.unbuffer = unbuffer; |
| } |
| |
| public boolean isUnbuffer() { |
| return unbuffer; |
| } |
| |
| public void setX11Forwarding(boolean x11forwarding) { |
| this.x11forwarding = x11forwarding; |
| } |
| |
| public boolean getX11Forwarding() { |
| return x11forwarding; |
| } |
| |
| public void setInitialSuspend(boolean suspend) { |
| this.suspend = suspend; |
| } |
| |
| public boolean getInitialSuspend() { |
| return suspend; |
| } |
| |
| public void setArguments(String... arguments) { |
| if (commandLine != null) { |
| throw new IllegalStateException("commandLine is already defined. No additional parameters can be set"); // NOI18N |
| } |
| |
| this.arguments.clear(); |
| |
| if (arguments != null) { |
| for (String arg : arguments) { |
| this.arguments.add(arg.trim()); |
| } |
| } |
| } |
| |
| public List<String> getArguments() { |
| return arguments; |
| } |
| |
| public String getExecutable() { |
| return executable; |
| } |
| |
| public List<String> getCommand() { |
| if (executable == null && commandLine == null) { |
| return null; |
| } |
| |
| List<String> result = new ArrayList<>(); |
| |
| String cmd; |
| |
| if (commandLine != null) { |
| try { |
| if (isExpandMacros()) { |
| cmd = macroExpander.expandPredefinedMacros(commandLine); |
| } else { |
| cmd = executable; |
| } |
| } catch (Exception ex) { |
| cmd = executable; |
| } |
| |
| result.add(cmd); |
| } else { |
| try { |
| if (isExpandMacros()) { |
| cmd = macroExpander.expandPredefinedMacros(executable); |
| } else { |
| cmd = executable; |
| } |
| } catch (Exception ex) { |
| cmd = executable; |
| } |
| |
| if (execEnv.isLocal()) { |
| cmd = findFullPathToExceutable(cmd); |
| } |
| |
| result.add(cmd); |
| |
| for (String arg : arguments) { |
| if (isExpandMacros()) { |
| arg = Utilities.escapeParameters(new String[]{arg}); |
| if ((arg.startsWith("'") && arg.endsWith("'")) || // NOI18N |
| (arg.startsWith("\"") && arg.endsWith("\""))) { // NOI18N |
| arg = arg.substring(1, arg.length() - 1); |
| } |
| result.add('"' + arg + '"'); // NOI18N |
| } else { |
| result.add(arg); |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| private String quoteSpecialChars(String orig) { |
| StringBuilder sb = new StringBuilder(); |
| String escapeChars = (isWindows) ? " &\"'()" : " &\"'()!"; // NOI18N |
| |
| for (char c : orig.toCharArray()) { |
| if (escapeChars.indexOf(c) >= 0) { // NOI18N |
| sb.append('\\'); |
| } |
| sb.append(c); |
| } |
| |
| return sb.toString(); |
| } |
| |
| public void setStatusEx(boolean getStatus) { |
| this.statusEx = getStatus; |
| } |
| |
| public String getCommandLineForShell() { |
| if (commandLine == null && executable == null) { |
| return null; |
| } |
| |
| /** |
| * See IZ#168186 - Wrongly interpreted "$" symbol in arguments |
| * |
| * The magic below is all about making run/debug act identically in case |
| * of ExternalTerminal |
| */ |
| if (commandLine != null) { |
| return commandLine; |
| } |
| |
| StringBuilder sb = new StringBuilder(); |
| |
| List<String> cmd = getCommand(); |
| |
| String exec = cmd.get(0); |
| |
| if (isWindows) { |
| exec = WindowsSupport.getInstance().convertToShellPath(exec); |
| |
| if (exec == null) { |
| return null; |
| } |
| } |
| |
| sb.append(quoteSpecialChars(exec)).append(' '); |
| |
| String[] sarg = new String[1]; |
| |
| boolean escape; |
| |
| for (String arg : arguments) { |
| escape = false; |
| sarg[0] = arg; |
| arg = Utilities.escapeParameters(sarg); |
| |
| sb.append('"'); |
| |
| if ((arg.startsWith("'") && arg.endsWith("'")) || // NOI18N |
| (arg.startsWith("\"") && arg.endsWith("\""))) { // NOI18N |
| arg = arg.substring(1, arg.length() - 1); |
| escape = true; |
| } |
| |
| if (isWindows || escape) { |
| char pc = 'x'; |
| |
| for (char c : arg.toCharArray()) { |
| if (c == '$' && pc != '\\') { |
| sb.append('\\'); |
| } |
| sb.append(c); |
| pc = c; |
| } |
| } else { |
| sb.append(arg); |
| } |
| |
| sb.append("\" "); // NOI18N |
| } |
| |
| return sb.toString().trim(); |
| } |
| |
| public ExecutionEnvironment getExecutionEnvironment() { |
| return execEnv; |
| } |
| |
| /* package */ Collection<ChangeListener> getListenersSnapshot() { |
| return new LinkedList<>(listeners); |
| } |
| |
| public String getWorkingDirectory(boolean expandMacros) { |
| String result = workingDirectory; |
| if (expandMacros && macroExpander != null) { |
| try { |
| result = macroExpander.expandPredefinedMacros(workingDirectory); |
| } catch (ParseException ex) { |
| // nothing |
| } |
| } |
| return result; |
| } |
| |
| public MacroMap getEnvironment() { |
| return environment; |
| } |
| |
| public void setPty(Pty pty) { |
| this.pty = pty; |
| runInPty = (pty != null); |
| } |
| |
| public Pty getPty() { |
| return pty; |
| } |
| |
| public void setPtyMode(boolean ptyMode) { |
| this.runInPty = ptyMode; |
| if (!ptyMode) { |
| pty = null; |
| } |
| } |
| |
| public boolean isPtyMode() { |
| return runInPty || getPty() != null; |
| } |
| |
| /** |
| * @return the expandMacros |
| */ |
| public boolean isExpandMacros() { |
| return expandMacros; |
| } |
| |
| /** |
| * @param expandMacros the expandMacros to set |
| */ |
| public void setExpandMacros(boolean expandMacros) { |
| this.expandMacros = expandMacros; |
| } |
| |
| public void setCharset(Charset charset) { |
| this.charset = charset; |
| } |
| |
| public Charset getCharset() { |
| return charset; |
| } |
| |
| private String findFullPathToExceutable(String cmd) { |
| if (execEnv.isRemote()) { |
| // Not going to search on remote ... |
| return cmd; |
| } |
| File f; |
| if ((!isWindows && cmd.startsWith("/"))) { // NOI18N |
| f = new File(cmd); |
| if (f.exists()) { |
| return f.getAbsolutePath(); |
| } |
| } |
| |
| if ((isWindows && cmd.length() > 2 && cmd.charAt(1) == ':')) { |
| f = new File(cmd); |
| if (f.exists()) { |
| return f.getAbsolutePath(); |
| } |
| f = new File(cmd + ".exe"); // NOI18N |
| if (f.exists()) { |
| return f.getAbsolutePath(); |
| } |
| } |
| |
| f = new File(workingDirectory, cmd); |
| |
| if (f.exists()) { |
| return f.getAbsolutePath(); |
| } |
| |
| if (isWindows) { |
| f = new File(workingDirectory, cmd + ".exe"); // NOI18N |
| |
| if (f.exists()) { |
| return f.getAbsolutePath(); |
| } |
| } |
| |
| return cmd; |
| } |
| |
| boolean isStatusEx() { |
| return statusEx; |
| } |
| |
| boolean isCommandLineDefined() { |
| return commandLine != null; |
| } |
| |
| public boolean isRedirectError() { |
| return redirectError; |
| } |
| |
| public static String getCharset(NativeProcess process) { |
| String res = null; |
| if (process instanceof AbstractNativeProcess) { |
| res = ((AbstractNativeProcess) process).getCharset(); |
| } |
| return res == null ? DEFAULT_CHARSET : res; |
| } |
| } |