tikhomirov@413: /* tikhomirov@413: * Copyright (c) 2012 TMate Software Ltd tikhomirov@413: * tikhomirov@413: * This program is free software; you can redistribute it and/or modify tikhomirov@413: * it under the terms of the GNU General Public License as published by tikhomirov@413: * the Free Software Foundation; version 2 of the License. tikhomirov@413: * tikhomirov@413: * This program is distributed in the hope that it will be useful, tikhomirov@413: * but WITHOUT ANY WARRANTY; without even the implied warranty of tikhomirov@413: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the tikhomirov@413: * GNU General Public License for more details. tikhomirov@413: * tikhomirov@413: * For information on how to redistribute this software under tikhomirov@413: * the terms of a license other than GNU General Public License tikhomirov@413: * contact TMate Software at support@hg4j.com tikhomirov@413: */ tikhomirov@413: package org.tmatesoft.hg.internal; tikhomirov@413: tikhomirov@413: import java.io.File; tikhomirov@413: import java.io.IOException; tikhomirov@413: import java.io.InputStreamReader; tikhomirov@413: import java.nio.CharBuffer; tikhomirov@413: import java.util.Arrays; tikhomirov@413: import java.util.LinkedList; tikhomirov@413: import java.util.List; tikhomirov@413: tikhomirov@413: /** tikhomirov@413: * Utility to run shell commands. Not thread-safe. tikhomirov@413: * Beware of memory overcommitment issue on Linux - suprocess get allocated virtual memory of parent process size tikhomirov@413: * @see http://developers.sun.com/solaris/articles/subprocess/subprocess.html tikhomirov@413: * tikhomirov@413: * @author Artem Tikhomirov tikhomirov@423: * @author TMate Software Ltd. tikhomirov@413: */ tikhomirov@413: public class ProcessExecHelper { tikhomirov@413: private File dir; tikhomirov@413: private int exitValue; tikhomirov@413: private ProcessBuilder pb; tikhomirov@413: tikhomirov@413: public ProcessExecHelper() { tikhomirov@413: } tikhomirov@413: tikhomirov@413: protected List prepareCommand(List cmd) { tikhomirov@413: return cmd; tikhomirov@413: } tikhomirov@413: tikhomirov@413: public CharSequence exec(String... command) throws IOException, InterruptedException { tikhomirov@413: return exec(Arrays.asList(command)); tikhomirov@413: } tikhomirov@413: tikhomirov@413: public CharSequence exec(List command) throws IOException, InterruptedException { tikhomirov@413: List cmd = prepareCommand(command); tikhomirov@413: if (pb == null) { tikhomirov@413: pb = new ProcessBuilder(cmd).directory(dir).redirectErrorStream(true); tikhomirov@413: } else { tikhomirov@413: pb.command(cmd); // dir and redirect are set tikhomirov@413: } tikhomirov@413: Process p = pb.start(); tikhomirov@413: InputStreamReader stdOut = new InputStreamReader(p.getInputStream()); tikhomirov@413: LinkedList l = new LinkedList(); tikhomirov@413: int r = -1; tikhomirov@413: CharBuffer b = null; tikhomirov@413: do { tikhomirov@413: if (b == null || b.remaining() < b.capacity() / 3) { tikhomirov@413: b = CharBuffer.allocate(512); tikhomirov@413: l.add(b); tikhomirov@413: } tikhomirov@413: r = stdOut.read(b); tikhomirov@413: } while (r != -1); tikhomirov@413: int total = 0; tikhomirov@413: for (CharBuffer cb : l) { tikhomirov@413: total += cb.position(); tikhomirov@413: cb.flip(); tikhomirov@413: } tikhomirov@413: CharBuffer res = CharBuffer.allocate(total); tikhomirov@413: for (CharBuffer cb : l) { tikhomirov@413: res.put(cb); tikhomirov@413: } tikhomirov@413: res.flip(); tikhomirov@413: p.waitFor(); tikhomirov@413: exitValue = p.exitValue(); tikhomirov@413: return res; tikhomirov@413: } tikhomirov@413: tikhomirov@413: public int exitValue() { tikhomirov@413: return exitValue; tikhomirov@413: } tikhomirov@413: tikhomirov@413: public ProcessExecHelper cwd(File wd) { tikhomirov@413: dir = wd; tikhomirov@413: if (pb != null) { tikhomirov@413: pb.directory(dir); tikhomirov@413: } tikhomirov@413: return this; tikhomirov@413: } tikhomirov@413: }