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