1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.internal.diffmergetool;
12
13 import java.io.File;
14 import java.io.FileOutputStream;
15 import java.io.IOException;
16 import java.io.OutputStream;
17 import java.nio.file.Files;
18 import java.nio.file.Path;
19 import java.nio.file.Paths;
20 import java.util.Arrays;
21 import java.util.Map;
22
23 import org.eclipse.jgit.errors.NoWorkTreeException;
24 import org.eclipse.jgit.util.FS;
25 import org.eclipse.jgit.util.FS.ExecutionResult;
26 import org.eclipse.jgit.util.FS_POSIX;
27 import org.eclipse.jgit.util.FS_Win32;
28 import org.eclipse.jgit.util.FS_Win32_Cygwin;
29 import org.eclipse.jgit.util.StringUtils;
30 import org.eclipse.jgit.util.SystemReader;
31
32
33
34
35 public class CommandExecutor {
36
37 private FS fs;
38
39 private boolean checkExitCode;
40
41 private File commandFile;
42
43 private boolean useMsys2;
44
45
46
47
48
49
50
51 public CommandExecutor(FS fs, boolean checkExitCode) {
52 this.fs = fs;
53 this.checkExitCode = checkExitCode;
54 }
55
56
57
58
59
60
61
62
63
64
65
66
67
68 public ExecutionResult run(String command, File workingDir,
69 Map<String, String> env)
70 throws ToolException, IOException, InterruptedException {
71 String[] commandArray = createCommandArray(command);
72 try {
73 ProcessBuilder pb = fs.runInShell(commandArray[0],
74 Arrays.copyOfRange(commandArray, 1, commandArray.length));
75 pb.directory(workingDir);
76 Map<String, String> envp = pb.environment();
77 if (env != null) {
78 envp.putAll(env);
79 }
80 ExecutionResult result = fs.execute(pb, null);
81 int rc = result.getRc();
82 if (rc != 0) {
83 boolean execError = isCommandExecutionError(rc);
84 if (checkExitCode || execError) {
85 throw new ToolException(
86 "JGit: tool execution return code: " + rc + "\n"
87 + "checkExitCode: " + checkExitCode + "\n"
88 + "execError: " + execError + "\n"
89 + "stderr: \n"
90 + new String(
91 result.getStderr().toByteArray(),
92 SystemReader.getInstance()
93 .getDefaultCharset()),
94 result, execError);
95 }
96 }
97 return result;
98 } finally {
99 deleteCommandArray();
100 }
101 }
102
103
104
105
106
107
108
109
110
111
112
113
114
115 public boolean checkExecutable(String path, File workingDir,
116 Map<String, String> env)
117 throws ToolException, IOException, InterruptedException {
118 checkUseMsys2(path);
119 String command = null;
120 if (fs instanceof FS_Win32 && !useMsys2) {
121 Path p = Paths.get(path);
122
123
124
125 if (p.isAbsolute() && Files.isExecutable(p)) {
126 return true;
127 }
128
129 command = "where " + ExternalToolUtils.quotePath(path);
130 } else {
131 command = "which " + ExternalToolUtils.quotePath(path);
132 }
133 boolean available = true;
134 try {
135 ExecutionResult rc = run(command, workingDir, env);
136 if (rc.getRc() != 0) {
137 available = false;
138 }
139 } catch (IOException | InterruptedException | NoWorkTreeException
140 | ToolException e) {
141
142 }
143 return available;
144 }
145
146 private void deleteCommandArray() {
147 deleteCommandFile();
148 }
149
150 private String[] createCommandArray(String command)
151 throws ToolException, IOException {
152 String[] commandArray = null;
153 checkUseMsys2(command);
154 createCommandFile(command);
155 if (fs instanceof FS_POSIX) {
156 commandArray = new String[1];
157 commandArray[0] = commandFile.getCanonicalPath();
158 } else if (fs instanceof FS_Win32) {
159 if (useMsys2) {
160 commandArray = new String[3];
161 commandArray[0] = "bash.exe";
162 commandArray[1] = "-c";
163 commandArray[2] = commandFile.getCanonicalPath().replace("\\",
164 "/");
165 } else {
166 commandArray = new String[1];
167 commandArray[0] = commandFile.getCanonicalPath();
168 }
169 } else if (fs instanceof FS_Win32_Cygwin) {
170 commandArray = new String[1];
171 commandArray[0] = commandFile.getCanonicalPath().replace("\\", "/");
172 } else {
173 throw new ToolException(
174 "JGit: file system not supported: " + fs.toString());
175 }
176 return commandArray;
177 }
178
179 private void checkUseMsys2(String command) {
180 useMsys2 = false;
181 String useMsys2Str = System.getProperty("jgit.usemsys2bash");
182 if (!StringUtils.isEmptyOrNull(useMsys2Str)) {
183 if (useMsys2Str.equalsIgnoreCase("auto")) {
184 useMsys2 = command.contains(".sh");
185 } else {
186 useMsys2 = Boolean.parseBoolean(useMsys2Str);
187 }
188 }
189 }
190
191 private void createCommandFile(String command)
192 throws ToolException, IOException {
193 String fileExtension = null;
194 if (useMsys2 || fs instanceof FS_POSIX
195 || fs instanceof FS_Win32_Cygwin) {
196 fileExtension = ".sh";
197 } else if (fs instanceof FS_Win32) {
198 fileExtension = ".cmd";
199 command = "@echo off" + System.lineSeparator() + command
200 + System.lineSeparator() + "exit /B %ERRORLEVEL%";
201 } else {
202 throw new ToolException(
203 "JGit: file system not supported: " + fs.toString());
204 }
205 commandFile = File.createTempFile(".__",
206 "__jgit_tool" + fileExtension);
207 try (OutputStream outStream = new FileOutputStream(commandFile)) {
208 byte[] strToBytes = command
209 .getBytes(SystemReader.getInstance().getDefaultCharset());
210 outStream.write(strToBytes);
211 outStream.close();
212 }
213 commandFile.setExecutable(true);
214 }
215
216 private void deleteCommandFile() {
217 if (commandFile != null && commandFile.exists()) {
218 commandFile.delete();
219 }
220 }
221
222 private boolean isCommandExecutionError(int rc) {
223 if (useMsys2 || fs instanceof FS_POSIX
224 || fs instanceof FS_Win32_Cygwin) {
225
226
227 if ((rc == 126) || (rc == 127)) {
228 return true;
229 }
230 }
231 else if (fs instanceof FS_Win32) {
232
233
234
235
236 if (rc == 9009) {
237 return true;
238 }
239 }
240 return false;
241 }
242
243 }